未验证 提交 f9acb734 编写于 作者: O openharmony_ci 提交者: Gitee

!989 docs: add some guidance documents for platform drivers.

Merge pull request !989 from 王亚枫/master
......@@ -10,7 +10,10 @@
- [平台驱动开发](driver-develop.md)
- [ADC](driver-platform-adc-develop.md)
- [GPIO](driver-platform-gpio-develop.md)
- [HDMI](driver-platform-hdmi-develop.md)
- [I2C](driver-platform-i2c-develop.md)
- [I3C](driver-platform-i3c-develop.md)
- [MIPI-CSI](driver-platform-mipicsi-develop.md)
- [MIPI-DSI](driver-platform-mipidsi-develop.md)
- [MMC](driver-platform-mmc-develop.md)
- [PWM](driver-platform-pwm-develop.md)
......@@ -19,18 +22,20 @@
- [SPI](driver-platform-spi-develop.md)
- [UART](driver-platform-uart-develop.md)
- [WatchDog](driver-platform-watchdog-develop.md)
- [MIPI_CSI](driver-platform-mipicsi-develop.md)
- [平台驱动使用](driver-platform.md)
- [ADC](driver-platform-adc-des.md)
- [GPIO](driver-platform-gpio-des.md)
- [HDMI](driver-platform-hdmi-des.md)
- [I2C](driver-platform-i2c-des.md)
- [I3C](driver-platform-i3c-des.md)
- [MIPI-CSI](driver-platform-mipicsi-des.md)
- [MIPI-DSI](driver-platform-mipidsi-des.md)
- [PWM](driver-platform-pwm-des.md)
- [RTC](driver-platform-rtc-des.md)
- [SDIO](driver-platform-sdio-des.md)
- [SPI](driver-platform-spi-des.md)
- [UART](driver-platform-uart-des.md)
- [WATCHDOG](driver-platform-watchdog-des.md)
- [MIPI DSI](driver-platform-mipidsi-des.md)
- [PWM](driver-platform-pwm-des.md)
- [MIPI_CSI](driver-platform-mipicsi-des.md)
- [外设驱动使用](driver-peripherals.md)
- [LCD](driver-peripherals-lcd-des.md)
- [TOUCHSCREEN](driver-peripherals-touch-des.md)
......@@ -38,4 +43,4 @@
- [WLAN](driver-peripherals-external-des.md)
- [AUDIO](driver-peripherals-audio-des.md)
- [USB](driver-peripherals-usb-des.md)
- [CAMERA](driver-peripherals-camera-des.md)
\ No newline at end of file
- [CAMERA](driver-peripherals-camera-des.md)
......@@ -4,8 +4,14 @@
- **[GPIO](driver-platform-gpio-develop.md)**
- **[HDMI](driver-platform-hdmi-develop.md)**
- **[I2C](driver-platform-i2c-develop.md)**
- **[I3C](driver-platform-i3c-develop.md)**
- **[MIPI-CSI](driver-platform-mipicsi-develop.md)**
- **[MIPI-DSI](driver-platform-mipidsi-develop.md)**
- **[MMC](driver-platform-mmc-develop.md)**
......@@ -21,5 +27,3 @@
- **[UART](driver-platform-uart-develop.md)**
- **[WatchDog](driver-platform-watchdog-develop.md)**
# ADC<a name="1"></a>
- [概述](#section1)
- [接口说明](#section2)
- [使用指导](#section3)
- [使用流程](#section4)
- [打开ADC设备](#section5)
- [读取AD转换结果](#section6)
- [关闭ADC设备](#section7)
- [使用实例](#section8)
## 概述<a name="section1"></a>
- ADC(Analog to Digital Converter),即模拟-数字转换器,是一种将模拟信号转换成对应数字信号的设备。
- ADC接口定义了完成ADC传输的通用方法集合,包括:
- ADC设备管理:打开或关闭ADC设备。
- ADC读取转换结果:读取AD转换结果。
**图 1** ADC物理连线示意图<a name="fig1"></a>
![](figures/ADC物理连线示意图.png "ADC物理连线示意图")
## 接口说明<a name="section2"></a>
**表 1** ADC驱动API接口功能介绍
<a name="table1"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="18.63%"><p>功能分类</p>
</th>
<th class="cellrowborder" valign="top" width="28.03%"><p>接口名</p>
</th>
<th class="cellrowborder" valign="top" width="53.339999999999996%"><p>描述</p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" bgcolor="#ffffff" rowspan="2" valign="top" width="18.63%"><p>ADC设备管理接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>AdcOpen</p>
</td>
<td class="cellrowborder" valign="top" width="53.339999999999996%">打开ADC设备</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>AdcClose</p>
</td>
<td valign="top"><p>关闭ADC设备</p>
</td>
</tr>
<tr><td class="cellrowborder" bgcolor="#ffffff" valign="top" width="18.63%"><p>ADC读取转换结果接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>AdcRead</p>
</td>
<td class="cellrowborder" valign="top" width="53.339999999999996%"><p>读取AD转换结果值</p>
</td>
</tr>
</table>
## 使用指导<a name="section3"></a>
### 使用流程<a name="section4"></a>
使用ADC设备的一般流程如[图2](#fig2)所示。
**图 2** ADC使用流程图<a name="fig2"></a>
![](figures/ADC使用流程图.png "ADC使用流程图")
### 打开ADC设备<a name="section5"></a>
在进行AD转换之前,首先要调用AdcOpen打开ADC设备。
```c
DevHandle AdcOpen(int16_t number);
```
**表 2** AdcOpen参数和返回值描述
<a name="table2"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="20.66%"><p>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="79.34%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="20.66%"><p>number</p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p>ADC设备号</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="20.66%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="20.66%"><p>NULL</p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p>打开ADC设备失败</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="20.66%"><p>设备句柄</p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p>打开的ADC设备句柄</p>
</td>
</tr>
</tbody>
</table>
假设系统中存在2个ADC设备,编号从0到1,那么我们现在打开1号设备。
```c
DevHandle adcHandle = NULL; /* ADC设备句柄 /
/* 打开ADC设备 */
adcHandle = AdcOpen(1);
if (adcHandle == NULL) {
HDF_LOGE("AdcOpen: failed\n");
return;
}
```
### 读取AD转换结果<a name="section6"></a>
```c
int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t *val);
```
**表 3** AdcRead参数和返回值描述
<a name="table3"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>ADC设备句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>channel</p>
</td>
<td class="cellrowborder"valign="top" width="50%"><p>ADC设备通道号</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>val</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>AD转换结果</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>读取成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>读取失败</p>
</td>
</tr>
</tbody>
</table>
### 关闭ADC设备<a name="section7"></a>
ADC通信完成之后,需要关闭ADC设备。
```c
void AdcClose(DevHandle handle);
```
**表 4** AdcClose参数和返回值描述
<a name="table4"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p>参数</p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p>参数描述</p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>ADC设备句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p></p>
</td>
</tr>
</tbody>
</table>
关闭ADC设备示例:
```c
AdcClose(adcHandle); /* 关闭ADC设备 */
```
## 使用实例<a name="section8"></a>
本例程以操作开发板上的ADC设备为例,详细展示ADC接口的完整使用流程。
本例拟对Hi3516DV300某开发板上ADC设备进行简单的读取操作,基本硬件信息如下:
- SOC:hi3516dv300。
- 原理图信息:电位器挂接在0号ADC设备1通道下。
本例程对测试ADC进行连续读取操作,测试ADC功能是否正常。
示例如下:
```c
#include "adc_if.h" /* ADC标准接口头文件 */
#include "hdf_log.h" /* 标准日志打印头文件 */
/* 设备号0,通道号1 */
#define ADC_DEVICE_NUM 0
#define ADC_CHANNEL_NUM 1
/* ADC例程总入口 */
static int32_t TestCaseAdc(void)
{
int32_t i;
int32_t ret;
DevHandle adcHandle;
uint32_t Readbuf[30] = {0};
/* 打开ADC设备 */
adcHandle = AdcOpen(ADC_DEVICE_NUM);
if (adcHandle == NULL) {
HDF_LOGE("%s: Open ADC%u fail!", __func__, ADC_DEVICE_NUM);
return -1;
}
/* 连续进行30次AD转换并读取转换结果 */
for (i = 0; i < 30; i++) {
ret = AdcRead(adcHandle, ADC_CHANNEL_NUM, &Readbuf[i]);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: tp ADC write reg fail!:%d", __func__, ret);
AdcClose(adcHandle);
return -1;
}
}
HDF_LOGI("%s: ADC read successful!", __func__);
/* 访问完毕关闭ADC设备 */
AdcClose(adcHandle);
return 0;
}
```
......@@ -9,8 +9,8 @@
ADC(Analog to Digital Converter),即模拟-数字转换器,是一种将模拟信号转换成对应数字信号的设备,在HDF框架中,ADC模块接口适配模式采用统一服务模式,这需要一个设备服务来作为ADC模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如ADC可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
**图 1** ADC统一服务<a name="fig14423182615525"></a>
![](figures/ADC统一服务.png "ADC统一服务")
**图 1** ADC统一服务模式<a name="fig14423182615525"></a>
![](figures/统一服务模式结构图.png "ADC统一服务模式")
## 接口说明<a name="section752964871810"></a>
......
......@@ -7,12 +7,10 @@
## 概述<a name="section1826197354103451"></a>
GPIO(General-purpose input/output)即通用型输入输出,在HDF框架中,
GPIO的接口适配模式采用无服务模式,用于不需要在用户态提供API的设备类型,或者没有用户态和内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址(DevHandle是一个void类型指针)。
GPIO(General-purpose input/output)即通用型输入输出,在HDF框架中,GPIO的接口适配模式采用无服务模式,用于不需要在用户态提供API的设备类型,或者没有用户态和内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址(DevHandle是一个void类型指针)。
**图 1** GPIO无服务模式结构图<a name="fig5511033193814"></a>
![](figures/GPIO无服务模式结构图.png "GPIO无服务模式结构图")
![](figures/无服务模式结构图.png "GPIO无服务模式结构图")
## 接口说明<a name="section752964871810"></a>
......
# HDMI<a name="1"></a>
- [概述](#section1)
- [接口说明](#section2)
- [使用指导](#section3)
- [使用流程](#section4)
- [打开HDMI控制器](#section5)
- [注册热插拔回调函数](#section6)
- [读取EDID](#section7)
- [设置属性](#section8)
- [启动HDMI传输](#section10)
- [停止HDMI传输](#section11)
- [注销热插拔回调函数](#section12)
- [关闭HDMI控制器](#section13)
- [使用实例](#section14)
## 概述<a name="section1"></a>
- HDMI(High-Definition Multiface Interface)是Hitachi, Panasonic, Philips, SiliconImage, Sony, Thomson, Toshiba共同发布的一款音视频传输协议。
- HDMI以主从方式工作,通常有一个Source端和一个Sink端。
- HDMI接口定义了完成HDMI传输的通用方法集合,包括:
- HDMI控制器管理:打开或关闭HDMI控制器
- HDMI启动/停止传输:启动或停止HDMI传输
- HDMI控制器设置:设置音频、视频及HDR属性,设置色彩深度、声音图像消隐等
- HDMI读取EDID:读取Sink端原始的EDID数据
- HDMI热插拔:注册/注销热插拔回调函数
- HDMI物理连接如[图1](#fig1)所示:
**图 1** HDMI物理连线示意图<a name="fig1"></a>
![](figures/HDMI物理连线示意图.png "HDMI物理连线示意图")
## 接口说明<a name="section2"></a>
**表 1** HDMI驱动API接口功能介绍
<a name="table1"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="18.63%"><p>功能分类</p>
</th>
<th class="cellrowborder" valign="top" width="28.03%"><p>接口名</p>
</th>
<th class="cellrowborder" valign="top" width="53.339999999999996%"><p>描述</p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" bgcolor="#ffffff" rowspan="2" valign="top" width="18.63%"><p>HDMI控制器管理接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>HdmiOpen</p>
</td>
<td class="cellrowborder" valign="top" width="53.339999999999996%">打开HDMI控制器</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>HdmiClose</p>
</td>
<td class="cellrowborder" valign="top"><p>关闭HDMI控制器</p>
</td>
</tr>
<tr><td class="cellrowborder" bgcolor="#ffffff" rowspan="2" valign="top" width="18.63%"><p>启动/停止HDMI传输接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>HdmiStart</p>
</td>
<td class="cellrowborder" valign="top" width="53.339999999999996%">启动HDMI传输</p>
</td>
</tr>
<tr id="row5632152611414"><td class="cellrowborder" valign="top"><p>HdmiStop</p>
</td>
<td class="cellrowborder" valign="top"><p>停止HDMI传输</p>
</td>
</tr>
<tr><td class="cellrowborder" bgcolor="#ffffff" rowspan="6" valign="top" width="18.63%"><p>HDMI控制器设置接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>HdmiAvmuteSet</p>
</td>
<td class="cellrowborder" valign="top" width="53.339999999999996%">HDMI声音图像消隐设置</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>HdmiDeepColorSet</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.2.4.1.2 "><p>设置色彩深度</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>HdmiDeepColorGet</p>
</td>
<td class="cellrowborder" valign="top"><p>获取色彩深度</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>HdmiSetVideoAttribute</p>
</td>
<td class="cellrowborder" valign="top"><p>设置视频属性</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>HdmiSetAudioAttribute</p>
</td>
<td class="cellrowborder" valign="top"><p>设置音频属性</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>HdmiSetHdrAttribute</p>
</td>
<td class="cellrowborder" valign="top"><p>设置HDR属性</p>
</td>
</tr>
<tr><td class="cellrowborder" bgcolor="#ffffff" valign="top" width="18.63%"><p>EDID获取接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>HdmiReadSinkEdid</p>
</td>
<td class="cellrowborder" valign="top" width="53.339999999999996%">HDMI读取Sink端原始EDID数据</p>
</td>
</tr>
<tr><td class="cellrowborder" bgcolor="#ffffff" rowspan="2" valign="top" width="18.63%"><p>HDMI热插拔相关接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>HdmiRegisterHpdCallbackFunc</p>
</td>
<td class="cellrowborder" valign="top" width="53.339999999999996%">注册HDMI热插拔检测回调函数</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>HdmiUnregisterHpdCallbackFunc</p>
</td>
<td class="cellrowborder" valign="top"><p>注销HDMI热插拔检测回调函数</p>
</td>
</tr>
</tbody>
</table>
## 使用指导<a name="section3"></a>
### 使用流程<a name="section4"></a>
使用HDMI设备的一般流程如[图2](#fig2)所示。
**图 2** HDMI设备使用流程图<a name="fig2"></a>
![](figures/HDMI使用流程图.png "HDMI使用流程图")
### 打开HDMI控制器<a name="section5"></a>
在进行HDMI通信前,首先要调用HdmiOpen打开HDMI控制器。
```c
DevHandle HdmiOpen(int16_t number);
```
**表 2** HdmiOpen参数和返回值描述
<a name="table2"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="20.66%"><p>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="79.34%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="20.66%"><p>number</p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p>HDMI控制器号</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="20.66%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="20.66%"><p>NULL</p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p>打开HDMI控制器失败</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="20.66%"><p>控制器句柄</p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p>打开的HDMI控制器句柄</p>
</td>
</tr>
</tbody>
</table>
假设系统中存在2个HDMI控制器,编号从0到1,那么我们现在获取0号控制器:
```c
DevHandle hdmiHandle = NULL; /* HDMI控制器句柄 /
/* 打开HDMI控制器 */
hdmiHandle = HdmiOpen(0);
if (hdmiHandle == NULL) {
HDF_LOGE("HdmiOpen: failed\n");
return;
}
```
### 注册热插拔检测回调函数<a name="section6"></a>
```c
int32_t HdmiRegisterHpdCallbackFunc(DevHandle handle, struct HdmiHpdCallbackInfo *callback);
```
**表 3** HdmiRegisterHpdCallbackFunc参数和返回值描述
<a name="table3"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>callback</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>热插拔回调函数信息</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>注册成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>注册失败</p>
</td>
</tr>
</tbody>
</table>
注册热插拔检测回调函数示例:
```c
/* 热插拔检测回调函数定义 */
static void HdmiHpdHandle(void *data, bool hpd)
{
if (data == NULL) {
HDF_LOGE("priv data is NULL");
return;
}
if (hpd == true) {
HDF_LOGD("HdmiHpdHandle: hot plug");
/* 调用者添加相关处理 */
} else {
HDF_LOGD("HdmiHpdHandle: hot unplog");
/* 调用者添加相关处理 */
}
}
/* 热插拔检测回调函数注册示例 */
struct HdmiHpdCallbackInfo info = {0};
info.data = handle;
info.callbackFunc = HdmiHpdHandle;
ret = HdmiRegisterHpdCallbackFunc(hdmiHandle, info);
if (ret != 0) {
HDF_LOGE("HdmiRegisterHpdCallbackFunc: Register failed.");
}
```
### 读取EDID<a name="section7"></a>
```c
int32_t HdmiReadSinkEdid(DevHandle handle, uint8_t *buffer, uint32_t len);
```
**表 4** HdmiReadSinkEdid参数和返回值描述
<a name="table4"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>buffer</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>数据缓冲区</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>len</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>数据长度</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>正整数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>成功读取的原始EDID数据</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数或0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>读取失败</p>
</td>
</tr>
</tbody>
</table>
读取Sink端的原始EDID数据示例:
```c
int32_t len;
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);
}
```
### 设置音频、视频及HDR属性<a name="section8"></a>
#### 设置音频属性
```c
int32_t HdmiSetAudioAttribute(DevHandle handle, struct HdmiAudioAttr *attr);
```
**表 5** HdmiSetAudioAttribute参数和返回值描述
<a name="table5"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>attr</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>音频属性</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>设置成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>设置失败</p>
</td>
</tr>
</tbody>
</table>
设置音频属性示例:
```c
struct HdmiAudioAttr audioAttr = {0};
int32_t ret;
audioAttr.codingType = HDMI_AUDIO_CODING_TYPE_MP3;
audioAttr.ifType = HDMI_AUDIO_IF_TYPE_I2S;
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.");
}
```
#### 设置视频属性
```c
int32_t HdmiSetVideoAttribute(DevHandle handle, struct HdmiVideoAttr *attr);
```
**表 6** HdmiSetVideoAttribute参数和返回值描述
<a name="table6"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>attr</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>视频属性</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>设置成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>设置失败</p>
</td>
</tr>
</tbody>
</table>
设置视频属性示例:
```c
struct HdmiVideoAttr videoAttr = {0};
int32_t ret;
videoAttr.colorSpace = HDMI_COLOR_SPACE_YCBCR444;
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.");
}
```
#### 设置HDR属性
```c
int32_t HdmiSetHdrAttribute(DevHandle handle, struct HdmiHdrAttr *attr);
```
**表 7** HdmiSetHdrAttribute参数和返回值描述
<a name="table7"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>attr</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDR属性</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>设置成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>设置失败</p>
</td>
</tr>
</tbody>
</table>
设置HDR属性示例:
```c
struct HdmiHdrAttr hdrAttr = {0};
int32_t ret;
hdrAttr.mode = HDMI_HDR_MODE_CEA_861_3;
hdrAttr.userMode = HDMI_HDR_USERMODE_DOLBY;
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.");
}
```
### 其他可选设置<a name="section9"></a>
#### 设置HDMI声音图像消隐
```c
int32_t HdmiAvmuteSet(DevHandle handle, bool enable);
```
**表 8** HdmiAvmuteSet参数和返回值描述
<a name="table8"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>enable</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>使能/去使能avmute</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>设置成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>设置失败</p>
</td>
</tr>
</tbody>
</table>
设置声音图像消隐示例:
```c
int32_t ret;
ret = HdmiAvmuteSet(hdmiHandle, true);
if (ret != 0) {
HDF_LOGE("HdmiAvmuteSet failed.");
}
```
#### 设置色彩深度
```c
int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color);
```
**表 9** HdmiDeepColorSet参数和返回值描述
<a name="table9"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>color</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>色彩深度</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>设置成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>设置失败</p>
</td>
</tr>
</tbody>
</table>
设置色彩深度示例:
```c
int32_t ret;
ret = HdmiDeepColorSet(handle, HDMI_DEEP_COLOR_48BITS);
if (ret != 0) {
HDF_LOGE("HdmiDeepColorSet failed.");
}
```
#### 获取色彩深度
```c
int32_t HdmiDeepColorGet(DevHandle handle, enum HdmiDeepColor *color);
```
**表 10** HdmiDeepColorGet参数和返回值描述
<a name="table10"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>color</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>色彩深度</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>获取成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>获取失败</p>
</td>
</tr>
</tbody>
</table>
获取色彩深度示例:
```c
enum HdmiDeepColor color;
int32_t ret;
ret = HdmiDeepColorGet(handle, &color);
if (ret != 0) {
HDF_LOGE("HdmiDeepColorGet failed.");
}
```
### 启动HDMI传输<a name="section10"></a>
```c
int32_t HdmiStart(DevHandle handle);
```
**表 11** HdmiStart参数和返回值描述
<a name="table11"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>启动成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>启动失败</p>
</td>
</tr>
</tbody>
</table>
启动HDMI传输示例:
```c
int32_t ret;
ret = HdmiStart(hdmiHandle);
if (ret != 0) {
HDF_LOGE("start transmission failed.");
}
```
### 停止HDMI传输<a name="section11"></a>
```c
int32_t HdmiStop(DevHandle handle);
```
**表 12** HdmiStop参数和返回值描述
<a name="table12"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>停止成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>停止失败</p>
</td>
</tr>
</tbody>
</table>
停止HDMI传输示例:
```c
int32_t ret;
ret = HdmiStop(hdmiHandle);
if (ret != 0) {
HDF_LOGE("stop transmission failed.");
}
```
### 注销热插拔检测回调函数<a name="section12"></a>
```c
int32_t HdmiUnregisterHpdCallbackFunc(DevHandle handle);
```
**表 13** HdmiUnregisterHpdCallbackFunc参数和返回值描述
<a name="table13"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>注销成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>注销失败</p>
</td>
</tr>
</tbody>
</table>
注销热插拔检测回调函数示例:
```c
int32_t ret;
ret = HdmiUnregisterHpdCallbackFunc(hdmiHandle);
if (ret != 0) {
HDF_LOGE("unregister failed.");
}
```
### 关闭HDMI控制器<a name="section13"></a>
```c
void HdmiClose(DevHandle handle);
```
**表 14** HdmiClose参数和返回值描述
<a name="table14"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>HDMI控制器句柄</p>
</td>
</tr>
</tbody>
</table>
关闭HDMI控制器示例:
```c
HdmiClose(hdmiHandle);
```
## 使用实例<a name="section14"></a>
本例程以操作开发板上的HDMI设备为例,详细展示HDMI接口的完整使用流程。
本例拟在Hi3516DV300开发板上对虚拟驱动进行简单的传输操作:
- SOC:hi3516dv300。
- HDMI控制器:使用0号HDMI控制器。
示例如下:
```c
#include "hdmi_if.h" /* HDMI标准接口头文件 */
#include "hdf_log.h" /* 标准日志打印头文件 */
#include "osal_time.h" /* 标准延迟&睡眠接口头文件 */
/* 热插拔回调函数 */
static void HdmiHpdHandle(void *data, bool hpd)
{
if (data == NULL) {
HDF_LOGE("priv data is NULL");
return;
}
if (hpd == true) {
HDF_LOGD("HdmiHpdHandle: hot plug");
/* 调用者添加相关处理 */
} else {
HDF_LOGD("HdmiHpdHandle: hot unplog");
/* 调用者添加相关处理 */
}
}
/* 设置HDMI相关属性 */
static int32_t TestHdmiSetAttr(DevHandle handle)
{
enum HdmiDeepColor color;
struct HdmiVideoAttr videoAttr = {0};
struct HdmiAudioAttr audioAttr = {0};
struct HdmiHdrAttr hdrAttr = {0};
int32_t ret;
ret = HdmiDeepColorSet(handle, HDMI_DEEP_COLOR_48BITS);
if (ret != 0) {
HDF_LOGE("HdmiDeepColorSet failed.");
return ret;
}
ret = HdmiDeepColorGet(handle, &color);
if (ret != 0) {
HDF_LOGE("HdmiDeepColorGet failed.");
return ret;
}
HDF_LOGE("HdmiDeepColorGet successful, color = %d.", color);
videoAttr.colorSpace = HDMI_COLOR_SPACE_YCBCR444;
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.");
return ret;
}
audioAttr.codingType = HDMI_AUDIO_CODING_TYPE_MP3;
audioAttr.ifType = HDMI_AUDIO_IF_TYPE_I2S;
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.");
return ret;
}
hdrAttr.mode = HDMI_HDR_MODE_CEA_861_3;
hdrAttr.userMode = HDMI_HDR_USERMODE_DOLBY;
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.");
return ret;
}
return 0;
}
/* HDMI例程总入口 */
static int32_t TestCaseHdmi(void)
{
DevHandle handle = NULL;
int32_t ret;
struct HdmiHpdCallbackInfo info = {0};
uint8_t data[128] = {0};
HDF_LOGD("HdmiAdapterInit: successful.");
handle = HdmiOpen(0);
if (handle == NULL) {
HDF_LOGE("HdmiOpen failed.");
return ret;
}
info.data = handle;
info.callbackFunc = HdmiHpdHandle;
ret = HdmiRegisterHpdCallbackFunc(handle, &info);
if (ret != 0) {
HDF_LOGE("HdmiRegisterHpdCallbackFunc failed.");
return ret;
}
ret = HdmiReadSinkEdid(handle, data, 128);
if (ret <= 0) {
HDF_LOGE("HdmiReadSinkEdid failed.");
return ret;
}
HDF_LOGE("HdmiReadSinkEdid successful, data[6] = %d, data[8] = %d.", data[6], data[8]);
ret = TestHdmiSetAttr(handle);
if (ret != 0) {
HDF_LOGE("TestHdmiSetAttr failed.");
return ret;
}
ret = HdmiStart(handle);
if (ret != 0) {
HDF_LOGE("HdmiStart failed.");
return ret;
}
OsalMSleep(1000);
ret = HdmiStop(handle);
if (ret != 0) {
HDF_LOGE("HdmiStop failed.");
return ret;
}
ret = HdmiUnregisterHpdCallbackFunc(handle);
if (ret != 0) {
HDF_LOGE("HdmiUnregisterHpdCallbackFunc failed.");
return ret;
}
HdmiClose(handle);
return 0;
}
```
\ No newline at end of file
# HDMI
- [概述](#1)
- [开发步骤](#2)
- [开发实例](#3)
## 概述 <a name="1"></a>
HDMI(High-Definition Multiface Interface)是Hitachi、Panasonic、Philips、SiliconImage、Sony、Thomson、Toshiba共同发布的一款音视频传输协议,主要用于DVD、机顶盒等音视频source到TV、显示器等sink设备的传输。传输过程遵循TMDS(Transition Minimized Differential Signaling)协议。
在HDF框架中,HDMI的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用率。
**图 1** HDMI统一服务模式<a name="fig1"></a>
![image1](figures/独立服务模式结构图.png)
## 开发步骤 <a name="2"></a>
HDMI模块适配的三个环节是配置属性文件,实例化驱动入口以及实例化HDMI控制器对象。
1. **实例化驱动入口:**
- 实例化HdfDriverEntry结构体成员。
- 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2. **配置属性文件:**
- 在device_info.hcs文件中添加deviceNode描述。
- 【可选】添加hdmi_config.hcs器件属性文件。
3. **实例化HDMI控制器对象:**
- 初始化HdmiCntlr成员。
- 实例化HdmiCntlr成员HdmiCntlrOps方法集合,其定义和成员函数说明见下文。
HdmiCntlrOps定义:
```c
struct HdmiCntlrOps {
void (*hardWareInit)(struct HdmiCntlr *cntlr);
void (*hardWareStatusGet)(struct HdmiCntlr *cntlr, struct HdmiHardwareStatus *status);
void (*controllerReset)(struct HdmiCntlr *cntlr);
bool (*hotPlugStateGet)(struct HdmiCntlr *cntlr);
bool (*hotPlugInterruptStateGet)(struct HdmiCntlr *cntlr);
void (*lowPowerSet)(struct HdmiCntlr *cntlr, bool enable);
void (*tmdsModeSet)(struct HdmiCntlr *cntlr, enum HdmiTmdsModeType mode);
int32_t (*tmdsConfigSet)(struct HdmiCntlr *cntlr, struct HdmiTmdsConfig mode);
void (*infoFrameEnable)(struct HdmiCntlr *cntlr, enum HdmiPacketType infoFrameType, bool enable);
int32_t (*infoFrameSend)(struct HdmiCntlr *cntlr, enum HdmiPacketType infoFrameType, uint8_t *data, uint32_t len);
int32_t (*infoFrameDataSet)(struct HdmiCntlr *cntlr, uint32_t type, uint8_t *data, uint32_t len);
int32_t (*cecMsgSend)(struct HdmiCntlr *cntlr, struct HdmiCecMsg *msg);
void (*audioPathEnable)(struct HdmiCntlr *cntlr, bool enable);
void (*audioPathSet)(struct HdmiCntlr *cntlr, struct HdmiAudioConfigInfo *config);
void (*phyOutputEnable)(struct HdmiCntlr *cntlr, bool enable);
void (*phyOutputSet)(struct HdmiCntlr *cntlr, struct HdmiPhyCfg *cfg);
void (*blackDataSet)(struct HdmiCntlr *cntlr, bool enable);
void (*videoMuteEnable)(struct HdmiCntlr *cntlr, bool enable);
void (*videoPathSet)(struct HdmiCntlr *cntlr, struct HdmiVideoAttr *attr);
void (*audioMuteEnable)(struct HdmiCntlr *cntlr, bool enable);
void (*avmuteSet)(struct HdmiCntlr *cntlr, bool enable);
int32_t (*ddcTransfer)(struct HdmiCntlr *cntlr, struct HdmiDdcCfg *ddcCfg);
bool (*scdcSourceScrambleGet)(struct HdmiCntlr *cntlr);
int32_t (*scdcSourceScrambleSet)(struct HdmiCntlr *cntlr, bool enable);
void (*frlSet)(struct HdmiCntlr *cntlr);
int32_t (*frlEnable)(struct HdmiCntlr *cntlr, bool enable);
int32_t (*audioNctsSet)(struct HdmiCntlr *cntlr, struct HdmiFrlAudioNctsConfig *cfg);
void (*frlTrainingConfigSet)(struct HdmiCntlr *cntlr, struct HdmiFrlTrainConfig *cfg);
void (*frlTrainingStart)(struct HdmiCntlr *cntlr);
void (*frlGetTriningRslt)(struct HdmiCntlr *cntlr, struct HdmiFrlTrainRslt *rslt);
void (*hdcpRegInit)(struct HdmiCntlr *cntlr);
int32_t (*hdcpGenerateAksvAndAn)(struct HdmiCntlr *cntlr);
int32_t (*hdcpOptReg)(struct HdmiCntlr *cntlr, enum HdmiHdcpRegOptType type, uint8_t *data, uint32_t len);
void (*hdrTimerSet)(struct HdmiCntlr *cntlr, struct HdmiHdrTimerConfig *config);
};
```
表1 HdmiCntlrOps结构体成员的回调函数功能说明
| 函数成员 | 入参 | 出参 | 返回值 | 功能 |
| ------------------------ | ------------------------------------------------------------ | -------------------------------------- | ------------------ | -------------------------------------------------- |
| hardWareInit | **cntlr**: 结构体指针,核心层HDMI控制器; | 无 | 无 | HDMI硬件初始化 |
| hardWareStatusGet | **cntlr**: 结构体指针,核心层HDMI控制器;<br /> | **status**:HDMI硬件状态 ; | 无 | 获取HDMI当前硬件状态 |
| controllerReset | **cntlr**: 结构体指针,核心层HDMI控制器; | 无 | 无 | HDMI控制器复位 |
| hotPlugStateGet | **cntlr**: 结构体指针,核心层HDMI控制器; | 无 | bool: HDMI热插拔状态 | 获取HDMI热插拔状态 |
| hotPlugInterruptStateGet | **cntlr**: 结构体指针,核心层HDMI控制器; | 无 | bool: HDMI热插拔中断状态 | 获取HDMI热插拔中断状态 |
| lowPowerSet | **cntlr**: 结构体指针,核心层HDMI控制器;<br />**enable**: bool,使能/去使能 | 无 | 无 | 使能/去使能低功耗 |
| tmdsModeSet | **cntlr**: 结构体指针,核心层HDMI控制器;<br />**mode**:TMDS模式 | 无 | 无 | 设置TMDS模式 |
|tmdsConfigSet|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**mode**: TMDS参数|无|HDF_STATUS相关状态|配置TMDS参数|
|infoFrameEnable|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**infoFrameType**: packet类型<br />**enable**: bool,使能/去使能|无|无|使能/去使能infoFrame|
|infoFrameSend|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**infoFrameType**: packet类型<br />**data**: infoFrame数据<br />**len**:数据长度|无|HDF_STATUS相关状态|发送inforFrame|
|cecMsgSend|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**msg**: CEC消息|无|HDF_STATUS相关状态|发送CEC消息|
|audioPathEnable|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**enable**: bool,使能/去使能|无|无|使能/去使能audio通路|
|audioPathSet|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**config**: 配置信息|无|无|设置audio通路配置信息|
|phyOutputEnable|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**enable**: bool,使能/去使能|无|无|使能/去使能物理层输出状态|
|phyOutputSet|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**cfg**: 配置信息|无|无|设置物理层配置信息|
|blackDataSet|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**enable**: bool,使能/去使能|无|无|黑屏设置|
|videoMuteEnable|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**enable**: bool,使能/去使能|无|无|使能/去使能video静音|
|videoPathSet|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**attr**: 配置信息|无|无|设置viedo通路配置信息|
|audioMuteEnable|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**enable**: bool,使能/去使能|无|无|使能/去使能audio静音|
|avmuteSet|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**enable**: bool,使能/去使能|无|无|使能/去使能声音图像消隐|
|ddcTransfer|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**ddcCfg**:DDC配置参数|**ddcCfg**:DDC配置参数|HDF_STATUS相关状态|DDC读写数据|
|scdcSourceScrambleGet|**cntlr**: 结构体指针,核心层HDMI控制器;|无|bool,加扰状态|获取source端的加扰状态|
|scdcSourceScrambleSet|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**enable**: bool,使能/去使能|无|HDF_STATUS相关状态|使能/去使能source端的加扰|
|frlEnable|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**enable**: bool,使能/去使能|无|HDF_STATUS相关状态|使能/去使能FRL|
|audioNctsSet|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**cfg**:N/CTS配 置参数|无|HDF_STATUS相关状态|设置audio的N/CTS信息|
|frlTrainingConfigSet|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**cfg**:FRL Traning配置参数|无|无|设置FRL Traning配置信息|
|frlTrainingStart|**cntlr**: 结构体指针,核心层HDMI控制器;|无|无|开始FRL Traning流程|
|frlGetTriningRslt|**cntlr**: 结构体指针,核心层HDMI控制器;|**rslt**:FRL Traning结果|无|获取FRL Traning结果|
|hdcpRegInit|**cntlr**: 结构体指针,核心层HDMI控制器;|无|无|初始化HDCP流程相关的寄存器|
|hdcpGenerateAksvAndAn|**cntlr**: 结构体指针,核心层HDMI控制器;|无|HDF_STATUS相关状态|HDCP流程中生成aksv和an|
|hdcpOptReg|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**type**: 操作类型<br />**data**: 寄存器数据<br />**len**: 数据长度|**data**: 寄存器数据|HDF_STATUS相关状态|HDCP流程中的相关寄存器读写操作|
|hdrTimerSet|**cntlr**: 结构体指针,核心层HDMI控制器;<br />**config**: timer配置信息|无|无|设置HDR相关的timer配置信息|
## 开发实例 <a name="3"></a>
1. 驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
HDMI驱动入口参考:
```c
struct HdfDriverEntry g_hdmiDriverEntry = {
.moduleVersion = 1,
.Bind = HdmiAdapterBind,
.Init = HdmiAdapterInit,
.Release = HdmiAdapterRelease,
.moduleName = "adapter_hdmi_driver",//【必要】与HCS里面的名字匹配
};
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 配置参考
```c
root {
platform :: host {
device_hdmi :: device {
device0 :: deviceNode {
policy = 2; // 等于2,需要发布服务
priority = 20; // 驱动启动优先级
permission = 0644; // 驱动创建设备节点权限
serviceName = "HDF_PLATFORM_HDMI_0"; //【必要】驱动对外发布服务的名称,必须唯一
moduleName = "hdmi_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致;
deviceMatchAttr = "adapter_hdmi_driver"; //【必要】用于配置控制器私有数据,要与hdmi_config.hcs中对应控制器保持一致
} // 具体的控制器信息在 hdmi_config.hcs 中
}
}
}
```
- hdmi_config.hcs 配置参考
```c
root {
platform {
hdmi_config {
template hdmi_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省
match_attr = ""; //【必要】需要和device_info.hcs中的deviceMatchAttr值一致
index = 0; //【必要】hdmi控制器编号
regBasePhy = 0x10100000; //【必要】寄存器物理基地址
regSize = 0xd1; //【必要】寄存器位宽
irqNum = 100; //【必要】中断号
maxTmdsClock = 300;
videoTiming = 10;
quantization = 1;
colorSpace = 0;
colorimetry = 0;
audioIfType = 0;
audioBitDepth = 1;
audioSampleRate = 2;
audioChannels = 1;
hdrColorimetry = 4;
hdrUserMode = 1;
cap = 0xd001e045;
}
controller_0x10100000 :: hdmi_controller {
match_attr = "adapter_hdmi_driver";
index = 0;
regBasePhy = 0x10100000;
irqNum = 100;
maxTmdsClock = 400;
defTmdsClock = 300;
maxFrlRate = 600;
videoTiming = 10;
quantization = 1;
colorSpace = 0;
colorimetry = 0;
audioIfType = 0;
audioSampleRate = 2;
audioChannels = 1;
hdrColorimetry = 4;
hdrUserMode = 1;
cap = 0xd001e045;
}
}
}
}
```
3. 最后一步,完成驱动入口注册之后,要以核心层HdmiCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化HdmiCntlr成员HdmiCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。
- 自定义结构体参考
> ![](../public_sys-resources/icon-note.gif) **说明:**
> 从驱动角度看,自定义结构体是参数和数据的载体。HDF会读取hdmi_config.hcs文件中的数值并通过DeviceResourceIface来初始化结构体成员,且其中一些重要数值(例如设备号、总线号等)也会被传递给核心层HdmiCntlr对象。
```c
struct HdmiAdapterHost {
struct HdmiCntlr *cntlr; //【必要】是核心层控制对象,具体描述如下
volatile unsigned char *regBase;//【必要】寄存器基地址
uint32_t regBasePhy; //【必要】寄存器物理基地址
uint32_t regSize; //【必要】寄存器位宽
uint32_t irqNum; //【必要】中断号
};
/* HdmiCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值 */
struct HdmiCntlr {
struct IDeviceIoService service;
struct HdfDeviceObject *hdfDevObj;
struct PlatformDevice device;
struct OsalMutex mutex;
struct PlatformQueue *msgQueue;
struct HdmiCntlrCap cap;
struct HdmiAttr attr;
struct HdmiCntlrOps *ops;
uint32_t deviceIndex;
uint32_t state; // 控制器状态
enum HdmiTmdsModeType tmdsMode;
struct HdmiDevice *hdmi;
struct HdmiInfoframe infoframe;
struct HdmiScdc *scdc;
struct HdmiDdc ddc;
struct HdmiFrl *frl;
struct HdmiHdcp *hdcp;
struct HdmiCec *cec;
struct HdmiEvent event;
struct HdmiHdr *hdr;
void *priv;
};
```
- **【重要】** HdmiCntlr成员回调函数结构体HdmiCntlrOps的实例化
```c
static struct HdmiCntlrOps g_hdmiAdapterHostOps = {
.hardWareInit = HdmiAdapterHardWareInit,
.hardWareStatusGet = HdmiAdapterHardWareStatusGet,
.controllerReset = HdmiAdapterControllerReset,
.hotPlugStateGet = HdmiAdapterHotPlugStateGet,
.hotPlugInterruptStateGet = HdmiAdapterHotPlugInterruptStateGet,
.lowPowerSet = HdmiAdapterLowPowerSet,
.tmdsModeSet = HdmiAdapterTmdsModeSet,
.tmdsConfigSet = HdmiAdapterTmdsConfigSet,
.infoframeEnable = HdmiAdapterInfoframeEnable,
.infoframeSend = HdmiAdapterInfoframeSend,
.infoframeDataSet = HdmiAdapterInfoframeDataSet,
.cecMsgSend = HdmiAdapterCecMsgSend,
.audioPathEnable = HdmiAdapterAudioPathEnable,
.audioPathSet = HdmiAdapterAudioPathSet,
.phyOutputEnable = HdmiAdapterPhyOutputEnable,
.phyOutputSet = HdmiAdapterPhyOutputSet,
.blackDataSet = HdmiAdapterBlackDataSet,
.videoMuteEnable = HdmiAdapterVideoMuteEnable,
.videoPathSet = HdmiAdapterVideoPathSet,
.audioMuteEnable = HdmiAdapterAudioMuteEnable,
.avmuteSet = HdmiAdapterAvmuteSet,
.ddcTransfer = HdmiAdapterDdcTransfer,
.scdcSourceScrambleGet = HdmiAdapterScdcSourceScrambleGet,
.scdcSourceScrambleSet = HdmiAdapterScdcSourceScrambleSet,
.frlSet = HdmiAdapterFrlSet,
.frlEnable = HdmiAdapterFrlEnable,
.audioNctsSet = HdmiAdapterAudioNctsSet,
.frlTrainingConfigSet = HdmiAdapterFrlTrainingConfigSet,
.frlTrainingStart = HdmiAdapterFrlTrainingStart,
.frlGetTriningRslt = HdmiAdapterFrlGetTriningRslt,
.hdcpRegInit = HdmiAdapterHdcpRegInit,
.hdcpGenerateAksvAndAn = HdmiAdapterHdcpGenerateAksvAndAn,
.hdcpOptReg = HdmiAdapterHdcpOptReg,
.hdrTimerSet = HdmiAdapterHdrTimerSet,
};
```
- **Bind函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外呈现的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> HDF_STATUS相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/framework/include/utils/hdf_base.h中HDF_STATUS 定义)
|状态(值)|状态描述|
|:-|:-|
|HDF_ERR_INVALID_OBJECT|控制器对象非法|
|HDF_ERR_INVALID_PARAM |参数非法|
|HDF_ERR_MALLOC_FAIL |内存分配失败|
|HDF_ERR_IO |I/O 错误|
|HDF_SUCCESS |传输成功|
|HDF_FAILURE |传输失败|
> **函数说明:**
> 初始化自定义结构体对象HdmiAdapterHost,初始化HdmiCntlr成员,调用核心层HdmiCntlrAdd函数。
>
> HdmiCntlr,HdmiAdapterHost,HdfDeviceObject之间互相赋值,方便其他函数可以相互转化。
```c
static int32_t HdmiAdapterBind(struct HdfDeviceObject *obj)
{
struct HdmiCntlr *cntlr = NULL;
struct HimciAdapterHost *host = NULL;
int32_t ret;
cntlr = (struct HdmiCntlr *)OsalMemCalloc(sizeof(struct HdmiCntlr));
if (cntlr == NULL) {
HDF_LOGE("%s: malloc cntlr failed!", __func__);
return HDF_ERR_MALLOC_FAIL;
}
host = (struct HimciAdapterHost *)OsalMemCalloc(sizeof(struct HimciAdapterHost));
if (host == NULL) {
HDF_LOGE("%s: malloc host failed!", __func__);
return HDF_ERR_MALLOC_FAIL;
}
cntlr->priv = (void *)host; //【必要】将host存放至cntlr的私有数据
cntlr->ops = &g_hdmiHostOps; //【必要】HdmiCntlrOps的实例化对象的挂载
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:
HdmiAdapterDeleteHost(host);
HDF_LOGD("HdmiAdapterBind: fail, err = %d.", ret);
return ret;
}
```
- **init函数参考**
>**入参:**
>HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
>**返回值:**
>HDF_STATUS相关状态
>
>函数说明:
>
>实现HdmiAdapterInit函数。
```c
static int32_t HdmiAdapterInit(struct HdfDeviceObject *obj)
{
return HDF_SUCCESS;
}
```
- **Release 函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> 无
>
> **函数说明:**
> 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给 Release 接口, 当HDF框架调用Init函数初始化驱动失败时,可以调用 Release 释放驱动资源。
```c
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的强制转化
}
```
> 所有强制转换获取相应对象的操作**前提**是在Init函数中具备对应赋值的操作。
......@@ -7,10 +7,10 @@
## 概述<a name="section2040078630114257"></a>
I2C\(Inter Integrated Circuit\)总线是由Philips公司开发的一种简单、双向二线制同步串行总线,在HDF框架中,I2C模块接口适配模式采用统一服务模式,这需要一个设备服务来作为I2C模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如I2C可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
I2C(Inter Integrated Circuit)总线是由Philips公司开发的一种简单、双向二线制同步串行总线,在HDF框架中,I2C模块接口适配模式采用统一服务模式,这需要一个设备服务来作为I2C模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如I2C可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
**图 1** I2C统一服务模式结构图<a name="fig17640124912440"></a>
![](figures/I2C统一服务模式结构图.png "I2C统一服务模式结构图")
![](figures/统一服务模式结构图.png "I2C统一服务模式结构图")
## 接口说明<a name="section752964871810"></a>
......
# I3C<a name="1"></a>
- [概述](#section1)
- [接口说明](#section2)
- [使用指导](#section3)
- [使用流程](#section4)
- [打开I3C控制器](#section5)
- [进行I3C通信](#section6)
- [获取I3C控制器配置](#section7)
- [配置I3C控制器](#section8)
- [请求IBI(带内中断)](#section9)
- [释放IBI(带内中断)](#section10)
- [关闭I3C控制器](#section11)
- [使用实例](#section12)
## 概述<a name="section1"></a>
- I3C(Improved Inter Integrated Circuit)总线是由MIPI Alliance开发的一种简单、低成本的双向二线制同步串行总线。
- I3C总线向下兼容传统的I2C设备,同时增加了带内中断(In-Bind Interrupt)功能,支持I3C设备进行热接入操作,弥补了I2C总线需要额外增加中断线来完成中断的不足。
- I3C总线上允许同时存在I2C设备、I3C从设备和I3C次级主设备。
- I3C接口定义了完成I3C传输的通用方法集合,包括:
- I3C控制器管理:打开或关闭I3C控制器。
- I3C控制器配置:获取或配置I3C控制器参数。
- I3C消息传输:通过消息传输结构体数组进行自定义传输。
- I3C带内中断:请求或释放带内中断。
- I3C的物理连接如[图1](#fig1)所示:
**图 1** I3C物理连线示意图<a name="fig1"></a>
![](figures/I3C物理连线示意图.png "I3C物理连线示意图")
## 接口说明<a name="section2"></a>
**表 1** I3C驱动API接口功能介绍
<a name="table1"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="18.63%"><p>功能分类</p>
</th>
<th class="cellrowborder" valign="top" width="28.03%"><p>接口名</p>
</th>
<th class="cellrowborder" valign="top" width="53.339999999999996%"><p>描述</p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" bgcolor="#ffffff" rowspan="2" valign="top" width="18.63%"><p>I3C控制器管理接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>I3cOpen</p>
</td>
<td class="cellrowborder" valign="top" width="53.339999999999996%">打开I3C控制器</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>I3cClose</p>
</td>
<td class="cellrowborder" valign="top"><p>关闭I3C控制器</p>
</td>
</tr>
<tr><td class="cellrowborder" bgcolor="#ffffff" valign="top" width="18.63%"><p>I3c消息传输接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>I3cTransfer</p>
</td>
<td class="cellrowborder" valign="top" width="53.339999999999996%"><p>自定义传输</p>
</td>
</tr>
<tr><td class="cellrowborder" bgcolor=ffffff rowspan="2" valign="top" width="18.63%"><p>I3C控制器配置接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>I3cSetConfig</p>
</td>
<td class="cellrowborder"valign="top" width="53.339999999999996%">配置I3C控制器</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>I3cGetConfig</p>
</td>
<td class="cellrowborder" valign="top"><p>获取I3C控制器配置</p>
</td>
</tr>
<tr><td class="cellrowborder" bgcolor=ffffff rowspan="2" valign="top" width="18.63%"><p>I3C带内中断接口</p>
</td>
<td class="cellrowborder" valign="top" width="28.03%"><p>I3cRequestIbi</p>
</td>
<td class="cellrowborder" valign="top" width="53.339999999999996%">请求带内中断</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top"><p>I3cFreeIbi</p>
</td>
<td class="cellrowborder" valign="top"><p>释放带内中断</p>
</td>
</tr>
</table>
>![](../public_sys-resources/icon-note.gif) **说明:**
>本文涉及的所有接口,仅限内核态使用,不支持在用户态使用。
## 使用指导<a name="section3"></a>
### 使用流程<a name="section4"></a>
I3C的使用流程如[图2](#fig2)所示。
**图 2** I3C使用流程图<a name="fig2"></a>
![](figures/I3C使用流程图.png "I3C使用流程图")
### 打开I3C控制器<a name="section5"></a>
在进行I3C通信前,首先要调用I3cOpen打开I3C控制器。
```c
DevHandle I3cOpen(int16_t number);
```
**表 2** I3cOpen参数和返回值描述
<a name="table2"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="20.66%"><p>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="79.34%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="20.66%"><p>number</p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p>I3C控制器号</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="20.66%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="20.66%"><p>NULL</p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p>打开I3C控制器失败</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="20.66%"><p>控制器句柄</p>
</td>
<td class="cellrowborder" valign="top" width="79.34%"><p>打开的I3C控制器句柄</p>
</td>
</tr>
</tbody>
</table>
假设系统中存在8个I3C控制器,编号从0到7,那么我们现在打开1号控制器:
```c
DevHandle i3cHandle = NULL; /* I3C控制器句柄 /
/* 打开I3C控制器 */
i3cHandle = I3cOpen(1);
if (i3cHandle == NULL) {
HDF_LOGE("I3cOpen: failed\n");
return;
}
```
### 进行I3C通信<a name="section6"></a>
消息传输
```c
int32_t I3cTransfer(DevHandle handle, struct I3cMsg *msgs, int16_t count, enum TransMode mode);
```
**表 3** I3cTransfer参数和返回值描述
<a name="table3"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>I3C控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>msgs</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>待传输数据的消息结构体数组</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>count</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>消息数组长度</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>mode</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>传输模式,0:I2C模式;1:I3C模式;2:发送CCC(Common Command Code)</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>正整数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>成功传输的消息结构体数目</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>执行失败</p>
</td>
</tr>
</tbody>
</table>
I3C传输消息类型为I3cMsg,每个传输消息结构体表示一次读或写,通过一个消息数组,可以执行若干次的读写组合操作。
```c
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 */
ret = I3cTransfer(i3cHandle, msgs, 2, I2C_MODE);
if (ret != 2) {
HDF_LOGE("I3cTransfer: failed, ret %d\n", ret);
return;
}
```
>![](../public_sys-resources/icon-caution.gif) **注意:**
>- I3cMsg结构体中的设备地址不包含读写标志位,读写信息由flags成员变量的读写控制位传递。
>- 本函数不对消息结构体个数做限制,其最大个数度由具体I3C控制器决定。
>- 本函数不对每个消息结构体中的数据长度做限制,同样由具体I3C控制器决定。
>- 本函数可能会引起系统休眠,禁止在中断上下文调用。
### 获取I3C控制器配置<a name="section7"></a>
```c
int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config);
```
**表 4** I3cGetConfig参数和返回值描述
<a name="table4"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>I3C控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>config</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>I3C控制器配置</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>获取成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>获取失败</p>
</td>
</tr>
</tbody>
</table>
### 配置I3C控制器<a name="section8"></a>
```c
int32_t I3cSetConfig(DevHandle handle, struct I3cConfig *config);
```
**表 5** I3cSetConfig参数和返回值描述
<a name="table5"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>I3C控制器句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>config</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>I3C控制器配置</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>配置成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>配置失败</p>
</td>
</tr>
</tbody>
</table>
### 请求IBI(带内中断)<a name="section9"></a>
```c
int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload);
```
**表 6** I3cRequestIbi参数和返回值描述
<a name="table6"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>I3C控制器设备句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>addr</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>I3C设备地址</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>func</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>IBI回调函数</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>payload</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>IBI有效载荷</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder"valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>请求成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>请求失败</p>
</td>
</tr>
</tbody>
</table>
```c
static int32_t TestI3cIbiFunc(DevHandle handle, uint16_t addr, struct I3cIbiData data)
{
(void)handle;
(void)addr;
HDF_LOGD("%s: %.16s", __func__, (char *)data.buf);
return 0;
}
int32_t I3cTestRequestIbi(void)
{
DevHandle i3cHandle = NULL;
int32_t ret;
/* 打开I3C控制器 */
i3cHandle = I3cOpen(1);
if (i3cHandle == NULL) {
HDF_LOGE("I3cOpen: failed\n");
return;
}
ret = I3cRequestIbi(i3cHandle, 0x3F, TestI3cIbiFunc, 16);
if (ret != 0) {
HDF_LOGE("%s: Requset IBI failed!", __func__);
return -1;
}
I3cClose(i3cHandle);
HDF_LOGD("%s: Done", __func__);
return 0;
}
```
### 释放IBI(带内中断)<a name="section10"></a>
```c
int32_t I3cFreeIbi(DevHandle handle, uint16_t addr);
```
**表 7** I3cFreeIbi参数和返回值描述
<a name="table7"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p><strong>参数</strong></p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p><strong>参数描述</strong></p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>I3C控制器设备句柄</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>addr</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>I3C设备地址</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p><strong>返回值</strong></p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p><strong>返回值描述</strong></p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>0</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>释放成功</p>
</td>
</tr>
<tr><td class="cellrowborder" valign="top" width="50%"><p>负数</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>释放失败</p>
</td>
</tr>
</tbody>
</table>
```c
I3cFreeIbi(i3cHandle, 0x3F); /* 释放带内中断 */
```
### 关闭I3C控制器<a name="section11"></a>
I3C通信完成之后,需要关闭I3C控制器,关闭函数如下所示:
```c
void I3cClose(DevHandle handle);
```
**表 4** I3cClose参数和返回值描述
<a name="table4"></a>
<table><thead align="left"><tr><th class="cellrowborder" valign="top" width="50%"><p>参数</p>
</th>
<th class="cellrowborder" valign="top" width="50%"><p>参数描述</p>
</th>
</tr>
</thead>
<tbody><tr><td class="cellrowborder" valign="top" width="50%"><p>handle</p>
</td>
<td class="cellrowborder" valign="top" width="50%"><p>I3C控制器设备句柄</p>
</td>
</tr>
</tbody>
</table>
```c
I3cClose(i3cHandle); /* 关闭I3C控制器 */
```
## 使用实例<a name="section12"></a>
本例程以操作开发板上的I3C设备为例,详细展示I3C接口的完整使用流程。
由于Hi3516DV300系列SOC没有I3C控制器,本例拟在Hi3516DV300某开发板上对虚拟驱动进行简单的传输操作,基本硬件信息如下:
- SOC:hi3516dv300。
- 虚拟:I3C地址为0x3f, 寄存器位宽为1字节。
- 原理图信息:虚拟I3C设备挂接在18号和19号I3C控制器下。
本例程进行简单的I3C传输,测试I3C通路是否正常。
示例如下:
```c
#include "i3c_if.h" /* I3C标准接口头文件 */
#include "i3c_ccc.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控制器句柄 */
};
/* 基于I3cTransfer方法封装一个寄存器读写的辅助函数, 通过flag表示读或写 */
static int 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 {
regBuf[index++] = (regAddr >> 8) & 0xFF;
regBuf[index++] = regAddr & 0xFF;
}
/* 填充I3cMsg消息结构 */
msgs[0].addr = testDevice->addr;
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].len = dataLen;
msgs[1].buf = regData;
if (I3cTransfer(testDevice->i3cHandle, msgs, 2, I2C_MODE) != 2) {
HDF_LOGE("%s: i3c read err", __func__);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
/* 寄存器读函数 */
static inline int 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,
unsigned char *regData, unsigned int dataLen)
{
return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 0);
}
/* 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.i3cHandle = NULL;
/* 打开I3C控制器 */
testDevice.i3cHandle = I3cOpen(testDevice.busNum);
if (testDevice.i3cHandle == NULL) {
HDF_LOGE("%s: Open I3c:%u fail!", __func__, testDevice.busNum);
return -1;
}
/* 向地址为0x3F的设备连续写7字节数据 */
ret = TestI3cWriteReg(&testDevice, 0x3F, bufWrite, 7);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: test i3c write reg fail!:%d", __func__, ret);
I3cClose(testDevice.i3cHandle);
return -1;
}
OsalMSleep(10);
/* 从地址为0x3F的设备连续读7字节数据 */
ret = TestI3cReadReg(&testDevice, 0x3F, bufRead, 7);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: test i3c read reg fail!:%d", __func__, ret);
I3cClose(testDevice.i3cHandle);
return -1;
}
HDF_LOGI("%s: test i3c write&read reg success!", __func__);
/* 访问完毕关闭I3C控制器 */
I3cClose(testDevice.i3cHandle);
return 0;
}
```
\ No newline at end of file
# I3C
- [概述](#1)
- [开发步骤](#2)
- [开发实例](#3)
## 概述 <a name="1"></a>
I3C(Improved Inter Integrated Circuit)总线是由MIPI Alliance开发的一种简单、低成本的双向二线制同步串行总线。在HDF框架中,I3C模块接口适配模式采用统一服务模式,这需要一个设备服务来作为I3C模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如I3C可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
![image1](figures/统一服务模式结构图.png)
## 开发步骤 <a name="2"></a>
I3C模块适配的四个环节是实例化驱动入口、配置属性文件、实例化I3C控制器对象以及注册中断处理子程序。
1. **实例化驱动入口:**
- 实例化HdfDriverEntry结构体成员。
- 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2. **配置属性文件:**
- 在device_info.hcs文件中添加deviceNode描述。
- 【可选】添加i3c_config.hcs器件属性文件。
3. **实例化I3C控制器对象:**
- 初始化I3cCntlr成员。
- 实例化I3cCntlr成员I3cMethod方法集合,其定义和成员函数说明见下文。
4. **注册中断处理子程序:**
为控制器注册中断处理程序,实现设备热接入和IBI(带内中断)功能。
I3cMethod定义:
```c
struct I3cMethod {
int32_t (*sendCccCmd)(struct I3cCntlr *cntlr, struct I3cCccCmd *ccc);
int32_t (*transfer)(struct I3cCntlr *cntlr, struct I3cMsg *msgs, int16_t count);
int32_t (*i2cTransfer)(struct I3cCntlr *cntlr, struct I3cMsg *msgs, int16_t count);
int32_t (*setConfig)(struct I3cCntlr *cntlr, struct I3cConfig *config);
int32_t (*getConfig)(struct I3cCntlr *cntlr, struct I3cConfig *config);
int32_t (*requestIbi)(struct I3cDevice *dev);
void (*freeIbi)(struct I3cDevice *dev);
};
```
表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|
## 开发实例 <a name="3"></a>
1. 驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
I3C驱动入口参考:
> I3C模块这种类型的控制器会出现很多个控制器挂接的情况,因而在HDF框架中首先会为这类型的控制器创建一个管理器对象,并同时对外发布一个管理器服务来统一处理外部访问。这样,用户需要打开某个控制器时,会先获取到管理器服务,然后管理器服务根据用户指定参数查找到指定控制器。
>
> I3C管理器服务的驱动由核心层实现,**厂商不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的I3cCntlrAdd函数,它会实现相应功能。**
```c
static struct HdfDriverEntry g_virtualI3cDriverEntry = {
.moduleVersion = 1,
.Init = VirtualI3cInit,
.Release = VirtualI3cRelease,
.moduleName = "virtual_i3c_driver",//【必要且与 HCS 里面的名字匹配】
};
HDF_INIT(g_virtualI3cDriverEntry); //调用HDF_INIT将驱动入口注册到HDF框架中
/* 核心层i3c_core.c管理器服务的驱动入口 */
struct HdfDriverEntry g_i3cManagerEntry = {
.moduleVersion = 1,
.Init = I3cManagerInit,
.Release = I3cManagerRelease,
.moduleName = "HDF_PLATFORM_I3C_MANAGER",//这与device_info文件中device0对应
};
HDF_INIT(g_i3cManagerEntry);
```
2. 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在i3c_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层I3cCntlr相关成员的默认值或限制范围有密切关系。
**统一服务模式**的特点是device_info文件中第一个设备节点必须为I3C管理器,其各项参数必须如下设置:
|成员名|值|
|-|-|
|moduleName |HDF_PLATFORM_I3C_MANAGER|
|serviceName|无(预留)|
|policy|0|
|cntlrMatchAttr| 无(预留)|
从第二个节点开始配置具体I3C控制器信息,此节点并不表示某一路I3C控制器,而是代表一个资源性质设备,用于描述一类I3C控制器的信息。本例只有一个I3C控制器,如有多个控制器,则需要在device_info文件增加deviceNode信息,以及在i3c_config文件中增加对应的器件属性。
- device_info.hcs 配置参考
```c
root {
device_i3c :: device {
device0 :: deviceNode {
policy = 0;
priority = 52;
permission = 0644;
serviceName = "HDF_PLATFORM_I3C_MANAGER";
moduleName = "HDF_PLATFORM_I3C_MANAGER";
}
}
i3c_virtual :: deviceNode {
policy = 0; // 等于0,不需要发布服务
priority = 56; // 驱动启动优先级
permission = 0644; // 驱动创建设备节点权限
moduleName = "virtual_i3c_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致;
serviceName = "VIRTUAL_I3C_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一
deviceMatchAttr = "virtual_i3c"; //【必要】用于配置控制器私有数据,要与i3c_config.hcs中对应控制器保持一致
} // 具体的控制器信息在 i3c_config.hcs 中
}
```
- i3c_config.hcs 配置参考
```c
root {
platform {
i3c_config {
match_attr = "virtual_i3c"; //【必要】需要和device_info.hcs中的deviceMatchAttr值一致
template i3c_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省
busId = 0; //【必要】i3c总线号
busMode = 0x0; // 总线模式,0x0:纯净; 0x1:混合高速; 0x2:混合受限; 0x3: 混合低速;
regBasePhy = 0x120b0000; //【必要】物理基地址
regSize = 0xd1; //【必要】寄存器位宽
IrqNum = 20; //【必要】中断号
i3cMaxRate = 12900000; //【可选】i3c模式最大时钟速率
i3cRate = 12500000; //【可选】i3c模式时钟速率
i2cFmRate = 1000000; //【可选】i2c FM模式时钟速率
i2cFmPlusRate = 400000; //【可选】i2c FM+模式时钟速率
}
controller_0 :: i3c_controller {
busId = 18;
IrqNum = 20;
}
}
}
}
```
3. 配置属性文件完成后,要以核心层I3cCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化I3cCntlr成员I3cMethod(让用户可以通过接口来调用驱动底层函数)。
此步骤需要通过实现HdfDriverEntry成员函数(Bind,Init,Release)来完成。
- 自定义结构体参考
> 从驱动的角度看,自定义结构体是参数和数据的载体,而且i3c_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I3cCntlr对象,例如设备号、总线号等。
```c
struct VirtualI3cCntlr {
struct I3cCntlr cntlr; //【必要】是核心层控制对象,具体描述见下面
volatile unsigned char *regBase;//【必要】寄存器基地址
uint32_t regBasePhy; //【必要】寄存器物理基地址
uint32_t regSize; //【必要】寄存器位宽
uint16_t busId; //【必要】设备号
uint16_t busMode;
uint16_t IrqNum;
uint32_t i3cMaxRate;
uint32_t i3cRate;
uint32_t i2cFmRate;
uint32_t i2cFmPlusRate;
};
/* I3cCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值 */
struct I3cCntlr {
OsalSpinlock lock;
void *owner;
int16_t busId;
struct I3cConfig config;
uint16_t addrSlot[(I3C_ADDR_MAX + 1) / ADDRS_PER_UINT16];
struct I3cIbiInfo *ibiSlot[I3C_IBI_MAX];
const struct I3cMethod *ops;
const struct I3cLockMethod *lockOps;
void *priv;
};
```
> **【重要】** I3cCntlr成员回调函数结构体I3cMethod的实例化,I3cLockMethod回调函数结构体本例未实现,若要实例化,可参考I2C驱动开发,其他成员在Init函数中初始化
- **init函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> HDF_STATUS相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/framework/include/utils/hdf_base.h中HDF_STATUS 定义)
|状态(值)|问题描述|
|:-|:-:|
|HDF_ERR_INVALID_OBJECT|控制器对象非法|
|HDF_ERR_INVALID_PARAM |参数非法|
|HDF_ERR_MALLOC_FAIL |内存分配失败|
|HDF_ERR_IO |I/O 错误|
|HDF_SUCCESS |传输成功|
|HDF_FAILURE |传输失败|
> **函数说明:**
> 初始化自定义结构体对象,初始化I3cCntlr成员,调用核心层I3cCntlrAdd函数。
```c
static int32_t VirtualI3cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node)
{
int32_t ret;
struct VirtualI3cCntlr *virtual = NULL; //【必要】自定义结构体对象
(void)device;
virtual = (struct VirtualI3cCntlr *)OsalMemCalloc(sizeof(*virtual)); //【必要】内存分配
if (virtual == NULL) {
HDF_LOGE("%s: Malloc virtual fail!", __func__);
return HDF_ERR_MALLOC_FAIL;
}
ret = VirtualI3cReadDrs(virtual, node); //【必要】将i3c_config文件的默认值填充到结构体中
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
goto __ERR__;
}
...
virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize);//【必要】地址映射
ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); //【必要】注册中断程序
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: register irq failed!", __func__);
return ret;
}
...
VirtualI3cCntlrInit(virtual); //【必要】I3C设备的初始化
virtual->cntlr.priv = (void *)node; //【必要】存储设备属性
virtual->cntlr.busId = virtual->busId; //【必要】初始化I3cCntlr成员
virtual->cntlr.ops = &g_method; //【必要】I3cMethod的实例化对象的挂载
(void)OsalSpinInit(&virtual->spin);
ret = I3cCntlrAdd(&virtual->cntlr); //【必要且重要】调用此函数将控制器添加至核心,返回成功信号后驱动才完全接入平台核心层
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: add i3c controller failed! ret = %d", __func__, ret);
(void)OsalSpinDestroy(&virtual->spin);
goto __ERR__;
}
return HDF_SUCCESS;
__ERR__: //若控制器添加失败,需要执行去初始化相关函数
if (virtual != NULL) {
OsalMemFree(virtual);
virtual = NULL;
}
return ret;
}
static int32_t VirtualI3cInit(struct HdfDeviceObject *device)
{
int32_t ret;
const struct DeviceResourceNode *childNode = NULL;
if (device == NULL || device->property == NULL) {
HDF_LOGE("%s: device or property is NULL", __func__);
return HDF_ERR_INVALID_OBJECT;
}
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
ret = VirtualI3cParseAndInit(device, childNode);
if (ret != HDF_SUCCESS) {
break;
}
}
return ret;
}
```
- **Release 函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息 。
>
> **返回值:**
> 无。
>
> **函数说明:**
> 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给 Release 接口, 当HDF框架调用Init函数初始化驱动失败时,可以调用 Release 释放驱动资源。所有强制转换获取相应对象的操作**前提**是在Init函数中具备对应赋值的操作。
```c
static void VirtualI3cRemoveByNode(const struct DeviceResourceNode *node)
{
int32_t ret;
int16_t busId;
struct I3cCntlr *cntlr = NULL;
struct VirtualI3cCntlr *virtual = NULL;
struct DeviceResourceIface *drsOps = NULL;
drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
if (drsOps == NULL || drsOps->GetUint32 == NULL) {
HDF_LOGE("%s: invalid drs ops fail!", __func__);
return;
}
ret = drsOps->GetUint16(node, "busId", (uint16_t *)&busId, 0);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: read busId fail!", __func__);
return;
}
...
/* 可以调用I3cCntlrGet函数通过设备的cntlrNum获取I3cCntlr对象, 以及调用I3cCntlrRemove函数来释放I3cCntlr对象的内容 */
cntlr = I3cCntlrGet(busId);
if (cntlr != NULL && cntlr->priv == node) {
I3cCntlrPut(cntlr);
I3cCntlrRemove(cntlr); //【必要】主要是从管理器驱动那边移除I3cCntlr对象
virtual = (struct VirtualI3cCntlr *)cntlr;//【必要】通过强制转换获取自定义的对象并进行release操作
(void)OsalSpinDestroy(&virtual->spin);
OsalMemFree(virtual);
}
return;
}
static void VirtualI3cRelease(struct HdfDeviceObject *device)
{
const struct DeviceResourceNode *childNode = NULL;
HDF_LOGI("%s: enter", __func__);
if (device == NULL || device->property == NULL) {
HDF_LOGE("%s: device or property is NULL", __func__);
return;
}
...
//遍历、解析i3c_config.hcs中的所有配置节点,并分别进行release操作
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
VirtualI3cRemoveByNode(childNode); //函数定义如上
}
}
```
4. 最后一步,实现中断处理程序,在中断处理程序中通过判断中断产生的地址,实现热接入、IBI等操作。
```c
static int32_t VirtualI3cReservedAddrWorker(struct VirtualI3cCntlr *virtual, uint16_t addr)
{
(void)virtual;
switch (addr) {
case I3C_HOT_JOIN_ADDR:
VirtualI3cHotJoin(virtual);
break;
case I3C_RESERVED_ADDR_7H3E:
case I3C_RESERVED_ADDR_7H5E:
case I3C_RESERVED_ADDR_7H6E:
case I3C_RESERVED_ADDR_7H76:
case I3C_RESERVED_ADDR_7H7A:
case I3C_RESERVED_ADDR_7H7C:
case I3C_RESERVED_ADDR_7H7F:
/* 广播地址单比特错误的所有情形 */
HDF_LOGW("%s: broadcast Address single bit error!", __func__);
break;
default:
HDF_LOGD("%s: Reserved address which is not supported!", __func__);
break;
}
return HDF_SUCCESS;
}
```
```c
static int32_t I3cIbiHandle(uint32_t irq, void *data)
{
struct VirtualI3cCntlr *virtual = NULL;
struct I3cDevice *device = NULL;
uint16_t ibiAddr;
char *testStr = "Hello I3C!";
(void)irq;
if (data == NULL) {
HDF_LOGW("%s: data is NULL!", __func__);
return HDF_ERR_INVALID_PARAM;
}
virtual = (struct VirtualI3cCntlr *)data;
/* 【必要】获取产生中断的地址,使用CHECK_RESERVED_ADDR宏判断该地址是否为I3C保留地址 */
ibiAddr = VirtualI3cGetIbiAddr();
if (CHECK_RESERVED_ADDR(ibiAddr) == I3C_ADDR_RESERVED) {
HDF_LOGD("%s: Calling VirtualI3cResAddrWorker...", __func__);
return VirtualI3cReservedAddrWorker(virtual, ibiAddr);
} else {
HDF_LOGD("%s: Calling I3cCntlrIbiCallback...", __func__);
device = GetDeviceByAddr(&virtual->cntlr, ibiAddr);
if (device == NULL) {
HDF_LOGE("func:%s device is NULL!",__func__);
return HDF_ERR_MALLOC_FAIL;
}
if (device->ibi->payload > VIRTUAL_I3C_TEST_STR_LEN) {
/* 将字符串"Hello I3C!"放入IBI缓冲区内 */
*device->ibi->data = *testStr;
}
/* 根据产生IBI的I3C设备调用IBI回调函数 */
return I3cCntlrIbiCallback(device);
}
return HDF_SUCCESS;
}
```
\ No newline at end of file
......@@ -7,10 +7,10 @@
## 概述 <a name="section1_MIPI_CSIDevelop"></a>
CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接口标准。在HDF框架中,MIPI-CSI的接口适配模式采用无服务模式,用于不需要在用户态提供API的设备类型,或者没有用户态和内核区分的OS系统,MIPI-CSI的接口关联方式是DevHandle直接指向设备对象内核态地址(DevHandle是一个void类型指针)。
CSI(Camera Serial Interface)是由MIPI(Mobile Industry Processor Interface )联盟下Camera工作组指定的接口标准。在HDF框架中,MIPI-CSI的接口适配模式采用无服务模式,用于不需要在用户态提供API的设备类型,或者没有用户态和内核区分的OS系统,MIPI-CSI的接口关联方式是DevHandle直接指向设备对象内核态地址(DevHandle是一个void类型指针)。
图 1 无服务模式结构图
![image1](figures/CSI无服务模式结构图.png)
![image1](figures/无服务模式结构图.png)
## 接口说明 <a name="section2_MIPI_CSIDevelop"></a>
......
......@@ -10,7 +10,7 @@
DSI(Display Serial Interface)是由移动行业处理器接口联盟(Mobile Industry Processor Interface \(MIPI\) Alliance)制定的规范。在HDF框架中,MIPI-DSI的接口适配模式采用无服务模式,用于不需要在用户态提供API的设备类型,或者没有用户态和内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址(DevHandle是一个void类型指针)。
**图 1** DSI无服务模式结构图<a name="fig207610236189"></a>
![](figures/DSI无服务模式结构图.png "DSI无服务模式结构图")
![](figures/无服务模式结构图.png "DSI无服务模式结构图")
## 接口说明<a name="section752964871810"></a>
......
......@@ -10,7 +10,7 @@
MMC(MultiMedia Card),即多媒体卡,在HDF框架中,MMC的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
**图 1** MMC独立服务模式结构图<a name="fig19517114132810"></a>
![](figures/MMC独立服务模式结构图.png "MMC独立服务模式结构图")
![](figures/独立服务模式结构图.png "MMC独立服务模式结构图")
## 接口说明<a name="section752964871810"></a>
......
......@@ -10,7 +10,7 @@
PWM(Pulse Width Modulator)即脉冲宽度调节器,在HDF框架中,PWM的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
**图 1** PWM独立服务模式结构图<a name="fig983655084219"></a>
![](figures/PWM独立服务模式结构图.png "PWM独立服务模式结构图")
![](figures/独立服务模式结构图.png "PWM独立服务模式结构图")
## 接口说明<a name="section752964871810"></a>
......
......@@ -10,7 +10,7 @@
RTC\(real-time clock\)为操作系统中的实时时钟设备,在HDF框架中,RTC的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
**图 1** RTC独立服务模式结构图<a name="fig6742142611299"></a>
![](figures/RTC独立服务模式结构图.png "RTC独立服务模式结构图")
![](figures/独立服务模式结构图.png "RTC独立服务模式结构图")
## 接口说明<a name="section752964871810"></a>
......
......@@ -7,10 +7,10 @@
## 概述<a name="section1347805272150053"></a>
SDIO由SD卡发展而来,被统称为mmc(MultiMediaCard),相关技术差别不大,在HDF框架中,SDIO的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
SDIO由SD卡发展而来,被统称为MMC(MultiMediaCard),相关技术差别不大,在HDF框架中,SDIO的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
**图 1** SDIO独立服务模式结构图<a name="fig124181331222"></a>
![](figures/SDIO独立服务模式结构图.png "SDIO独立服务模式结构图")
![](figures/独立服务模式结构图.png "SDIO独立服务模式结构图")
## 接口说明<a name="section752964871810"></a>
......
......@@ -7,10 +7,10 @@
## 概述<a name="section84922229152909"></a>
SPI是串行外设接口(Serial Peripheral Interface)的缩写,在HDF框架中,SPI的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
SPI即串行外设接口(Serial Peripheral Interface),在HDF框架中,SPI的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
**图 1** SPI独立服务模式结构图<a name="fig666465313303"></a>
![](figures/SPI独立服务模式结构图.png "SPI独立服务模式结构图")
![](figures/独立服务模式结构图.png "SPI独立服务模式结构图")
## 接口说明<a name="section752964871810"></a>
......
......@@ -10,7 +10,7 @@
UART是通用异步收发传输器(Universal Asynchronous Receiver/Transmitter)的缩写,在HDF框架中,UART的接口适配模式采用独立服务模式。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
**图 1** UART独立服务模式结构图<a name="fig1474518243468"></a>
![](figures/UART独立服务模式结构图.png "UART独立服务模式结构图")
![](figures/独立服务模式结构图.png "UART独立服务模式结构图")
## 接口说明<a name="section752964871810"></a>
......
......@@ -10,7 +10,7 @@
看门狗(Watchdog),又叫看门狗计时器(Watchdog timer),是一种硬件的计时设备,在HDF框架中,Watchdog接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
**图 1** Watchdog独立服务模式结构图<a name="fig61584136211"></a>
![](figures/Watchdog独立服务模式结构图.png "Watchdog独立服务模式结构图")
![](figures/独立服务模式结构图.png "Watchdog独立服务模式结构图")
## 接口说明<a name="section752964871810"></a>
......
# 平台驱动使用<a name="ZH-CN_TOPIC_0000001111199424"></a>
- **[ADC](driver-platform-adc-des.md)**
- **[GPIO](driver-platform-gpio-des.md)**
- **[HDMI](driver-platform-hdmi-des.md)**
- **[I2C](driver-platform-i2c-des.md)**
- **[I3C](driver-platform-i3c-des.md)**
- **[MIPI-CSI](driver-platform-mipicsi-des.md)**
- **[MIPI-DSI](driver-platform-mipidsi-des.md)**
- **[PWM](driver-platform-pwm-des.md)**
- **[RTC](driver-platform-rtc-des.md)**
- **[SDIO](driver-platform-sdio-des.md)**
......@@ -13,9 +25,3 @@
- **[UART](driver-platform-uart-des.md)**
- **[WATCHDOG](driver-platform-watchdog-des.md)**
- **[MIPI DSI](driver-platform-mipidsi-des.md)**
- **[PWM](driver-platform-pwm-des.md)**
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册