提交 eeb2ff76 编写于 作者: Y yafeng_wang

docs: Document rectification

Signed-off-by: Nyafeng_wang <wangyafeng15@huawei.com>
上级 91832e5b
......@@ -4,65 +4,61 @@
### 功能简介<a name="section2"></a>
ADC(Analog to Digital Converter),即模拟-数字转换器,是一种将模拟信号转换成对应数字信号的设备。
ADC(Analog to Digital Converter),即模拟-数字转换器,可将模拟信号转换成对应的数字信号,便于存储与计算等操作。除电源线和地线之外,ADC只需要1根线与被测量的设备进行连接,其物理连线如图1:
**图 1** ADC物理连线示意图<a name="fig1"></a>
![](figures/ADC物理连线示意图.png "ADC物理连线示意图")
ADC接口定义了完成AD转换的通用方法集合,包括:
ADC接口定义了完成ADC传输的通用方法集合,包括:
- ADC设备管理:打开或关闭ADC设备。
- ADC读取转换结果:读取AD转换结果。
### 基本概念<a name="section3"></a>
ADC主要用于将模拟量转换成数字量,从而便于存储与计算等。
ADC的主要技术参数有:
- 分辨率
分辨率指的是ADC模块能够转换的二进制位数,位数越多分辨率越高。
- 转换误差
转换误差通常是以输出误差的最大值形式给出。它表示A/D转换器实际输出的数字量和理论上的输出数字量之间的差别。常用最低有效位的倍数表示。
- 转换时间
转换时间是指A/D转换器从转换控制信号到来开始,到输出端得到稳定的数字信号所经过的时间。
### 运作机制<a name="section4"></a>
在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),如果采用独立服务模式则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。ADC模块接口适配模式采用统一服务模式。
ADC模块各分层的作用为:接口层提供打开设备,写入数据,关闭设备的接口。核心层主要提供绑定设备、初始化设备以及释放设备的能力。适配层实现其他具体的功能。
除电源线和地线之外,ADC只需要1根线与被测量的设备进行连接,其物理连线如[图1](#fig1)所示:
**图 1** ADC物理连线示意图<a name="fig1"></a>
![](figures/ADC物理连线示意图.png "ADC物理连线示意图")
### 约束与限制<a name="section5"></a>
ADC模块当前仅支持轻量和小型系统内核(LiteOS)
ADC模块仅支持轮询方式读取数据
## 使用指导<a name="section6"></a>
### 场景介绍<a name="section7"></a>
ADC设备通常用于将模拟电压转换为数字量,如与咪头搭配进行声音采集、与NTC电阻搭配进行温度测量,或者将其他模拟传感器的输出量转换为数字量的场景。
ADC设备通常用于将模拟电压或电流转换为数字量,例如与NTC电阻搭配进行温度测量,或者将其他模拟传感器的输出量转换为数字量的场景。
### 接口说明<a name="section8"></a>
ADC模块提供的主要接口如[表1](#table1)所示,更多关于接口的介绍请参考对应的API接口文档
ADC模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/adc_if.h
**表 1** ADC驱动API接口功能介绍
<a name="table1"></a>
| 接口名 | 描述 |
| 接口名 | 接口描述 |
| -------- | ---------------- |
| AdcOpen | 打开ADC设备 |
| AdcClose | 关闭ADC设备 |
| AdcRead | 读取AD转换结果值 |
| DevHandle AdcOpen(uint32_t number) | 打开ADC设备 |
| void AdcClose(DevHandle handle) | 关闭ADC设备 |
| int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t \*val) | 读取AD转换结果值 |
### 开发步骤<a name="section9"></a>
使用ADC设备的一般流程如[图2](#fig2)所示。
使用ADC设备的一般流程如图2所示。
**图 2** ADC使用流程图<a name="fig2"></a>
![](figures/ADC使用流程图.png "ADC使用流程图")
......@@ -156,13 +152,11 @@ AdcClose(adcHandle); /* 关闭ADC设备 */
### 使用实例<a name="section10"></a>
本例程以操作开发板上的ADC设备为例,详细展示ADC接口的完整使用流程。
本例拟对Hi3516DV300某开发板上ADC设备进行简单的读取操作,基本硬件信息如下:
本例拟对Hi3516DV300开发板上ADC设备进行简单的读取操作,基本硬件信息如下:
- SOC:hi3516dv300。
- 原理图信息:电位器挂接在0号ADC设备1通道下。
- 硬件连接:电位器挂接在0号ADC设备1通道下。
本例程对测试ADC进行连续读取操作,测试ADC功能是否正常。
......@@ -175,14 +169,15 @@ AdcClose(adcHandle); /* 关闭ADC设备 */
/* 设备号0,通道号1 */
#define ADC_DEVICE_NUM 0
#define ADC_CHANNEL_NUM 1
#define ADC_TEST_NUM 30
/* ADC例程总入口 */
static int32_t TestCaseAdc(void)
{
int32_t i;
int32_t ret;
DevHandle adcHandle;
uint32_t readBuf[30] = {0};
DevHandle adcHandle = NULL;
uint32_t readBuf[ADC_TEST_NUM] = {0};
/* 打开ADC设备 */
adcHandle = AdcOpen(ADC_DEVICE_NUM);
......@@ -192,7 +187,7 @@ static int32_t TestCaseAdc(void)
}
/* 连续进行30次AD转换并读取转换结果 */
for (i = 0; i < 30; i++) {
for (i = 0; i < ADC_TEST_NUM; i++) {
ret = AdcRead(adcHandle, ADC_CHANNEL_NUM, &readBuf[i]);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: ADC read fail!:%d", __func__, ret);
......
......@@ -4,7 +4,10 @@
### 功能简介
DAC(Digital to Analog Converter)是一种通过电流、电压或电荷的形式将数字信号转换为模拟信号的设备。
DAC(Digital to Analog Converter)是一种通过电流、电压或电荷的形式将数字信号转换为模拟信号的设备,主要用于:
- 作为过程控制计算机系统的输出通道,与执行器相连,实现对生产过程的自动控制。
- 在利用反馈技术的模数转换器设计中,作为重要的功能模块呈现。
DAC接口定义了完成DAC传输的通用方法集合,包括:
- DAC设备管理:打开或关闭DAC设备。
......@@ -12,11 +15,6 @@ DAC接口定义了完成DAC传输的通用方法集合,包括:
### 基本概念
DAC模块支持数模转换的开发,它主要用于:
1. 作为过程控制计算机系统的输出通道,与执行器相连,实现对生产过程的自动控制。
2. 在利用反馈技术的模数转换器设计中,作为重要的功能模块呈现。
- 分辨率
分辨率指的是DAC模块能够转换的二进制位数,位数越多分辨率越高。
......@@ -35,7 +33,7 @@ DAC模块支持数模转换的开发,它主要用于:
### 运作机制
在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),如果采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。DAC模块接口适配模式采用统一服务模式,如图1所示。
在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),如果采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。DAC模块接口适配模式采用统一服务模式(如图1)。
DAC模块各分层的作用为:接口层提供打开设备、写入数据和关闭设备的接口。核心层主要提供绑定设备、初始化设备以及释放设备的能力。适配层实现其它具体的功能。
......@@ -47,7 +45,7 @@ DAC模块各分层的作用为:接口层提供打开设备、写入数据和
### 约束与限制
DAC模块当前仅支持轻量和小型系统内核(LiteOS)。
DAC模块当前仅支持轻量和小型系统内核(LiteOS-A)。
## 使用指导
......@@ -57,11 +55,11 @@ DAC模块的主要工作是以电流、电压或电荷的形式将数字信号
### 接口说明
DAC模块提供的主要接口如下所示,更多关于接口的介绍请参考对应的API接口文档
DAC模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/framework/include/platform/dac_if.h
**表 1** DAC驱动API接口功能介绍
| 接口名 | 描述 |
| 接口名 | 接口描述 |
| ------------------------------------------------------------------ | ------------ |
| DevHandle DacOpen(uint32_t number) | 打开DAC设备。 |
| void DacClose(DevHandle handle) | 关闭DAC设备。 |
......@@ -94,7 +92,7 @@ DevHandle DacOpen(uint32_t number);
假设系统中存在2个DAC设备,编号从0到1,现在打开1号设备。
```c++
DevHandle dacHandle = NULL; /* DAC设备句柄 /
DevHandle dacHandle = NULL; // DAC设备句柄
/* 打开DAC设备 */
dacHandle = DacOpen(1);
......@@ -123,12 +121,12 @@ int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val);
```c++
/* 通过DAC_CHANNEL_NUM设备通道写入目标val值 */
ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val);
if (ret != HDF_SUCCESS) {
ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: tp DAC write reg fail!:%d", __func__, ret);
DacClose(dacHandle);
return -1;
}
}
```
#### 关闭DAC设备
......
......@@ -3,9 +3,13 @@
## 概述
I2C(Inter Integrated Circuit)总线是由Philips公司开发的一种简单、双向二线制同步串行总线。
### 功能简介
I2C以主从方式工作,通常有一个主设备和一个或者多个从设备,主从设备通过SDA(SerialData)串行数据线以及SCL(SerialClock)串行时钟线两根线相连,如图1所示。
I2C(Inter Integrated Circuit)总线是由Philips公司开发的一种简单、双向二线制同步串行总线。由于其硬件连接简单、成本低廉,因此被广泛应用于各种短距离通信的场景。
### 运作机制
I2C以主从方式工作,通常有一个主设备和一个或者多个从设备,主从设备通过SDA(SerialData)串行数据线以及SCL(SerialClock)串行时钟线两根线相连(如图1)。
I2C数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。数据传输以字节为单位,高位在前,逐个bit进行传输。
......@@ -19,37 +23,40 @@ I2C接口定义了完成I2C传输的通用方法集合,包括:
![image](figures/I2C物理连线示意图.png "I2C物理连线示意图")
## 使用指导
## 接口说明
### 场景介绍
**表1** I2C驱动API接口功能介绍
I2C通常用于与各类支持I2C协议的传感器、执行器或输入输出设备进行通信。
| 功能分类 | 接口描述 |
| -------- | -------- |
| I2C控制器管理接口 | -&nbsp;I2cOpen:打开I2C控制器<br/>-&nbsp;I2cClose:关闭I2C控制器 |
| I2C消息传输接口 | I2cTransfer:自定义传输 |
### 接口说明
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
> 本文涉及的所有接口,仅限内核态使用,不支持在用户态使用。
I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/i2c_if.h。
**表1** I2C驱动API接口功能介绍
## 使用指导
| 接口名 | 接口描述 |
| -------- | -------- |
| DevHandle I2cOpen(int16_t number) | 打开I2C控制器 |
| void I2cClose(DevHandle handle) | 关闭I2C控制器 |
| int32_t I2cTransfer(DevHandle handle, struct I2cMsg \*msgs, int16_t count) | 自定义传输 |
### 使用流程
使用I2C设备的一般流程如下图所示。
**图2** I2C设备使用流程图
**图2** I2C设备使用流程图
![image](figures/I2C设备使用流程图.png "I2C设备使用流程图")
![image](figures/I2C设备使用流程图.png "I2C设备使用流程图")
### 打开I2C控制器
#### 打开I2C控制器
在进行I2C通信前,首先要调用I2cOpen打开I2C控制器。
```c
DevHandle I2cOpen(int16_t number);
```
**表2** I2cOpen参数和返回值描述
......@@ -62,8 +69,7 @@ DevHandle I2cOpen(int16_t number);
假设系统中存在8个I2C控制器,编号从0到7,以下代码示例为获取3号控制器:
```
```c
DevHandle i2cHandle = NULL; /* I2C控制器句柄 /
/* 打开I2C控制器 */
......@@ -75,11 +81,13 @@ if (i2cHandle == NULL) {
```
### 进行I2C通信
#### 进行I2C通信
消息传输
```c
int32_t I2cTransfer(DevHandle handle, struct I2cMsg \*msgs, int16_t count);
```
**表3** I2cTransfer参数和返回值描述
......@@ -92,10 +100,10 @@ int32_t I2cTransfer(DevHandle handle, struct I2cMsg \*msgs, int16_t count);
| 正整数 | 成功传输的消息结构体数目 |
| 负数 | 执行失败 |
I2C传输消息类型为I2cMsg,每个传输消息结构体表示一次读或写,通过一个消息数组,可以执行若干次的读写组合操作。
I2C传输消息类型为I2cMsg,每个传输消息结构体表示一次读或写,通过一个消息数组,可以执行若干次的读写组合操作。组合读写示例:
```
```c
int32_t ret;
uint8_t wbuff[2] = { 0x12, 0x13 };
uint8_t rbuff[2] = { 0 };
......@@ -126,11 +134,13 @@ if (ret != 2) {
> - 本函数可能会引起系统休眠,不允许在中断上下文调用
### 关闭I2C控制器
#### 关闭I2C控制器
I2C通信完成之后,需要关闭I2C控制器,关闭函数如下所述:
```c
void I2cClose(DevHandle handle);
```
**表4** I2cClose参数和返回值描述
......@@ -138,23 +148,24 @@ void I2cClose(DevHandle handle);
| -------- | -------- |
| handle | I2C控制器设备句柄 |
关闭I2C控制器示例:
```
```c
I2cClose(i2cHandle); /* 关闭I2C控制器 */
```
## 使用示例
### 使用示例
本例程以操作开发板上的I2C设备为例,详细展示I2C接口的完整使用流程。
本例拟对Hi3516DV300开发板上TouchPad设备进行简单的寄存器读写访问,基本硬件信息如下:
本例拟对Hi3516DV300开发板上TouchPad设备进行简单的寄存器读写访问,基本硬件信息如下:
- SOC:hi3516dv300。
- Touch IC:I2C地址为0x38, IC内部寄存器位宽为1字节。
- Touch IC:I2C地址为0x38IC内部寄存器位宽为1字节。
- 原理图信息:TouchPad设备挂接在3号I2C控制器下;IC的复位管脚为3号GPIO。
- 硬件连接:TouchPad设备挂接在3号I2C控制器下;IC的复位管脚为3号GPIO。
本例程首先对Touch IC进行复位操作(开发板上电默认会给TouchIC供电,本例程不考虑供电),然后对其内部寄存器进行随机读写,测试I2C通路是否正常。
......@@ -163,8 +174,7 @@ I2cClose(i2cHandle); /* 关闭I2C控制器 */
示例如下:
```
```c
#include "i2c_if.h" /* I2C标准接口头文件 */
#include "gpio_if.h" /* GPIO标准接口头文件 */
#include "hdf_log.h" /* 标准日志打印头文件 */
......
......@@ -35,7 +35,7 @@ I3C接口定义了完成I3C传输的通用方法集合,包括:
### 运作机制<a name="section4"></a>
在HDF框架中,I3C模块接口适配模式采用统一服务模式,这需要一个设备服务来作为I3C模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如I3C可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
在HDF框架中,I3C模块接口适配模式采用统一服务模式,这需要一个设备服务来作为I3C模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如I3C可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。
相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。一路I3C总线上,可以连接多个设备,这些设备可以是I2C从设备、I3C从设备和I3C次级主设备,但只能同时存在一个主设备,一般为控制器本身。
......@@ -44,38 +44,41 @@ I3C接口定义了完成I3C传输的通用方法集合,包括:
### 约束与限制<a name="section5"></a>
I3C模块当前仅支持轻量和小型系统内核(LiteOS
I3C模块当前仅支持轻量和小型系统内核(LiteOS-A),不支持在用户态使用
## 使用指导<a name="section6"></a>
### 场景介绍<a name="section7"></a>
I3C可连接单个或多个I3C、I2C从器件,它主要用于:
1. 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等;
2. 通过软件或硬件协议转换,与其他接口(如 UART 串口等)的设备进行通信。
- 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等;
- 通过软件或硬件协议转换,与其他接口(如 UART 串口等)的设备进行通信。
### 接口说明<a name="section8"></a>
I3C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/i3c_if.h。
**表 1** I3C驱动API接口功能介绍
<a name="table1"></a>
| 接口名 | 描述 |
| 接口名 | 接口描述 |
| ------------- | ----------------- |
| I3cOpen | 打开I3C控制器 |
| I3cClose | 关闭I3C控制器 |
| I3cTransfer | 自定义传输 |
| I3cSetConfig | 配置I3C控制器 |
| I3cGetConfig | 获取I3C控制器配置 |
| I3cRequestIbi | 请求带内中断 |
| I3cFreeIbi | 释放带内中断 |
| DevHandle I3cOpen(int16_t number) | 打开I3C控制器 |
| void I3cClose(DevHandle handle) | 关闭I3C控制器 |
| int32_t I3cTransfer(DevHandle handle, struct I3cMsg \*msg, int16_t count, enum TransMode mode) | 自定义传输 |
| int32_t I3cSetConfig(DevHandle handle, struct I3cConfig \*config) | 配置I3C控制器 |
| int32_t I3cGetConfig(DevHandle handle, struct I3cConfig \*config) | 获取I3C控制器配置 |
| int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload) | 请求带内中断 |
| int32_t I3cFreeIbi(DevHandle handle, uint16_t addr) | 释放带内中断 |
>![](../public_sys-resources/icon-note.gif) **说明:**<br>
>本文涉及的所有接口,仅限内核态使用,不支持在用户态使用。
### 开发步骤<a name="section9"></a>
I3C的使用流程如[图2](#fig2)所示。
I3C的使用流程如图2所示。
**图 2** I3C使用流程图<a name="fig2"></a>
![](figures/I3C使用流程图.png "I3C使用流程图")
......@@ -111,65 +114,15 @@ if (i3cHandle == NULL) {
}
```
#### 进行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>
| 参数 | 参数描述 |
| ---------- | -------------------------------------------- |
| handle | I3C控制器句柄 |
| msgs | 待传输数据的消息结构体数组 |
| count | 消息数组长度 |
| mode | 传输模式,0:I2C模式;1:I3C模式;2:发送CCC |
| **返回值** | **返回值描述** |
| 正整数 | 成功传输的消息结构体数目 |
| 负数 | 执行失败 |
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参数和返回值描述
**表 3** I3cGetConfig参数和返回值描述
<a name="table4"></a>
<a name="table3"></a>
| 参数 | 参数描述 |
| ---------- | -------------- |
......@@ -197,9 +150,9 @@ if (ret != HDF_SUCCESS) {
int32_t I3cSetConfig(DevHandle handle, struct I3cConfig *config);
```
**表 5** I3cSetConfig参数和返回值描述
**表 4** I3cSetConfig参数和返回值描述
<a name="table5"></a>
<a name="table4"></a>
| 参数 | 参数描述 |
| ---------- | -------------- |
......@@ -223,6 +176,56 @@ if (ret != HDF_SUCCESS) {
}
```
#### 进行I3C通信<a name="section6"></a>
消息传输
```c
int32_t I3cTransfer(DevHandle handle, struct I3cMsg *msgs, int16_t count, enum TransMode mode);
```
**表 5** I3cTransfer参数和返回值描述
<a name="table5"></a>
| 参数 | 参数描述 |
| ---------- | -------------------------------------------- |
| handle | I3C控制器句柄 |
| msgs | 待传输数据的消息结构体数组 |
| count | 消息数组长度 |
| mode | 传输模式,0:I2C模式;1:I3C模式;2:发送CCC |
| **返回值** | **返回值描述** |
| 正整数 | 成功传输的消息结构体数目 |
| 负数 | 执行失败 |
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控制器决定。
>- 本函数可能会引起系统休眠,禁止在中断上下文调用。
#### 请求IBI(带内中断)<a name="section9"></a>
```c
......@@ -310,9 +313,9 @@ I3C通信完成之后,需要关闭I3C控制器,关闭函数如下所示:
void I3cClose(DevHandle handle);
```
**表 4** I3cClose参数和返回值描述
**表 8** I3cClose参数和返回值描述
<a name="table4"></a>
<a name="table8"></a>
| 参数 | 参数描述 |
| ---------- | -------------- |
......@@ -326,15 +329,13 @@ I3cClose(i3cHandle); /* 关闭I3C控制器 */
## 使用实例<a name="section10"></a>
本例程以操作开发板上的I3C设备为例,详细展示I3C接口的完整使用流程。
由于Hi3516DV300系列SOC没有I3C控制器,本例拟在Hi3516DV300某开发板上对虚拟驱动进行简单的传输操作,基本硬件信息如下:
本例程以操作Hi3516DV300开发板上的I3C虚拟设备为例,详细展示I3C接口的完整使用流程,基本硬件信息如下。
- SOC:hi3516dv300。
- 虚拟:I3C地址为0x3f, 寄存器位宽为1字节。
- 虚拟I3C设备:I3C地址为0x3f, 寄存器位宽为1字节。
- 原理图信息:虚拟I3C设备挂接在18号和19号I3C控制器下。
- 硬件连接:虚拟I3C设备挂接在18号和19号I3C控制器下。
本例程进行简单的I3C传输,测试I3C通路是否正常。
......
......@@ -6,7 +6,7 @@
I3C(Improved Inter Integrated Circuit)总线是由MIPI Alliance开发的一种简单、低成本的双向二线制同步串行总线。
I3C是两线双向串行总线,针对多个传感器从设备进行了优化,并且一次只能由一个I3C主设备控制。 相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。
I3C是两线双向串行总线,针对多个传感器从设备进行了优化,并且一次只能由一个I3C主设备控制。相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。I3C增加了带内中断(In-Bind Interrupt)功能,支持I3C设备进行热接入操作,弥补了I2C总线需要额外增加中断线来完成中断的不足。I3C总线上允许同时存在I2C设备、I3C从设备和I3C次级主设备。
### 基本概念<a name="3"></a>
......@@ -16,11 +16,11 @@ I3C是两线双向串行总线,针对多个传感器从设备进行了优化
- DAA(Dynamic Address Assignment):动态地址分配。
I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C设备都应以两种方式之一来唯一标识:
I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C/I2C设备都应以两种方式之一来唯一标识:
- 设备可能有一个符合I2C规范的静态地址,主机可以使用此静态地址。
- 在任何情况下,设备均应具有48位的临时ID。除非设备具有静态地址且主机使用静态地址,否则主机应使用此48位临时ID。
- 在任何情况下,I3C设备均应具有48位的临时ID。除非设备具有静态地址且主机使用静态地址,否则主机应使用此48位临时ID。
- CCC(Common Command Code):通用命令代码。
......@@ -37,31 +37,39 @@ I3C是两线双向串行总线,针对多个传感器从设备进行了优化
### 运作机制<a name="4"></a>
在HDF框架中,同类型控制器对象较多时(可能同时存在十几个同类型控制器),如果采用独立服务模式则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。I3C模块接口适配模式采用统一服务模式(如图1所示)。
在HDF框架中,同类型控制器对象较多时(可能同时存在十几个同类型控制器),如果采用独立服务模式则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。I3C模块采用统一服务模式(如图1)。
I3C模块各分层的作用为:
- 接口层提供打开控制器、传输消息、获取和设置控制器参数以及关闭控制器的接口。
- 核心层主要提供绑定设备、初始化设备以及释放设备的能力。
- 适配层实现其他具体的功能。
**图 1** I3C统一服务模式<a name="fig1"></a>
- 接口层:提供打开设备,写入数据,关闭设备的能力。
- 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。由于框架需要统一管理I3C总线上挂载的所有设备,因此还提供了添加、删除以及获取设备的能力,以及中断回调函数。
- 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
**图 1** I3C统一服务模式结构图<a name="fig1"></a>
![image1](figures/统一服务模式结构图.png)
### 约束与限制<a name="5"></a>
I3C模块当前仅支持轻量和小型系统内核(LiteOS) 。
I3C模块当前仅支持轻量和小型系统内核(LiteOS-A) 。
## 开发指导 <a name="6"></a>
### 场景介绍 <a name="7"></a>
I3C可连接单个或多个I3C、I2C从器件,它主要用于:
- 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等。
- 通过软件或硬件协议转换,与其他通信接口(如UART串口等)的设备进行通信。
当驱动开发者需要将I3C设备适配到OpenHarmony时,需要进行I3C驱动适配,下文将介绍如何进行I3C驱动适配。
### 接口说明 <a name="8"></a>
为了保证上层在调用I3C接口时能够正确的操作硬件,核心层在//drivers/hdf_core/framework/support/platform/include/i3c/i3c_core.h中定义了以下钩子函数。驱动适配者需要在适配层实现这些函数的具体功能,并与这些钩子函数挂接,从而完成接口层与核心层的交互。
I3cMethod定义:
```c
struct I3cMethod {
......@@ -75,7 +83,7 @@ struct I3cMethod {
};
```
**表1** I3cMethod结构体成员的回调函数功能说明
**表1** I3cMethod结构体成员的钩子函数功能说明
|函数成员|入参|出参|返回值|功能|
|-|-|-|-|-|
|sendCccCmd| **cntlr**:结构体指针,核心层I3C控制器<br />**ccc**:传入的通用命令代码结构体指针 | **ccc**:传出的通用命令代码结构体指针 | HDF_STATUS相关状态|发送CCC(Common command Code,即通用命令代码)|
......@@ -108,17 +116,16 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
1. 实例化驱动入口
驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
驱动入口必须为HdfDriverEntry(在//drivers/hdf_core/framework/include/core/hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
I3C驱动入口参考:
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
> I3C控制器会出现很多个控制器挂接的情况,因而在HDF框架中首先会为此类型的控制器创建一个管理器对象,并同时对外发布一个管理器服务来统一处理外部访问。这样,用户需要打开某个控制器时,会先获取到管理器服务,然后管理器服务根据用户指定参数查找到指定控制器。
>
> I3C管理器服务的驱动由核心层实现,厂商不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的I3cCntlrAdd函数,它会实现相应功能。
> I3C管理器服务的驱动由核心层实现,驱动适配者不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的I3cCntlrAdd函数,它会实现相应功能。
```c
static struct HdfDriverEntry g_virtualI3cDriverEntry = {
......@@ -134,18 +141,18 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
.moduleVersion = 1,
.Init = I3cManagerInit,
.Release = I3cManagerRelease,
.moduleName = "HDF_PLATFORM_I3C_MANAGER",// 这与device_info文件中device0对应
.moduleName = "HDF_PLATFORM_I3C_MANAGER", // 这与device_info.hcs文件中device0对应
};
HDF_INIT(g_i3cManagerEntry);
```
2. 配置属性文件
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在i3c_config.hcs中配置器件属性。
完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在i3c_config.hcs中配置器件属性。
deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层I3cCntlr相关成员的默认值或限制范围有密切关系。
deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层I3cCntlr相关成员的默认值或限制范围有密切关系。
统一服务模式的特点是device_info文件中第一个设备节点必须为I3C管理器,其各项参数必须如下设置:
统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为I3C管理器,其各项参数必须如下设置:
|成员名|值|
|-|-|
......@@ -154,7 +161,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
|policy|0|
|cntlrMatchAttr| 无(预留)|
从第二个节点开始配置具体I3C控制器信息,此节点并不表示某一路I3C控制器,而是代表一个资源性质设备,用于描述一类I3C控制器的信息。本例只有一个I3C控制器,如有多个控制器,则需要在device_info文件增加deviceNode信息,以及在i3c_config文件中增加对应的器件属性。
从第二个节点开始配置具体I3C控制器信息,此节点并不表示某一路I3C控制器,而是代表一个资源性质设备,用于描述一类I3C控制器的信息。本例只有一个I3C控制器,如有多个控制器,则需要在device_info.hcs文件增加deviceNode信息,以及在i3c_config文件中增加对应的器件属性。
- device_info.hcs配置参考
......@@ -207,13 +214,21 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
}
```
需要注意的是,新增i3c_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
例如:本例中i3c_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i3c/i3c_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
```c
#include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i3c/i3c_config.hcs" // 配置文件相对路径
```
3. 实例化I3C控制器对象
配置属性文件完成后,要以核心层I3cCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化I3cCntlr成员I3cMethod(让用户可以通过接口来调用驱动底层函数)。
配置属性文件完成后,要以核心层I3cCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化I3cCntlr成员I3cMethod(让用户可以通过接口来调用驱动底层函数)。
此步骤需要通过实现HdfDriverEntry成员函数(Bind,Init,Release)来完成。
I3cCntlr成员回调函数结构体I3cMethod的实例化,I3cLockMethod回调函数结构体本例未实现,若要实例化,可参考I2C驱动开发,其他成员在Init函数中初始化。
I3cCntlr成员钩子函数结构体I3cMethod的实例化,I3cLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发,其他成员在Init函数中初始化。
- 自定义结构体参考
......@@ -223,7 +238,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
```c
struct VirtualI3cCntlr {
struct I3cCntlr cntlr; // 【必要】是核心层控制对象,具体描述见下面。
volatile unsigned char *regBase;// 【必要】寄存器基地址
volatile unsigned char *regBase; // 【必要】寄存器基地址
uint32_t regBasePhy; // 【必要】寄存器物理基地址
uint32_t regSize; // 【必要】寄存器位宽
uint16_t busId; // 【必要】设备号
......@@ -249,17 +264,15 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
};
```
- Init函数参考
- Init函数开发参考
**入参:**
HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
**返回值:**
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/framework/include/utils/hdf_base.h中HDF_STATUS 定义)。
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS 定义)。
|状态(值)|问题描述|
......@@ -288,7 +301,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
return HDF_ERR_MALLOC_FAIL;
}
ret = VirtualI3cReadDrs(virtual, node); // 【必要】将i3c_config文件的默认值填充到结构体中
ret = VirtualI3cReadDrs(virtual, node); // 【必要】将i3c_config文件的默认值填充到结构体中,函数定义见下方
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret);
goto __ERR__;
......@@ -342,13 +355,40 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
return ret;
}
static int32_t VirtualI3cReadDrs(struct VirtualI3cCntlr *virtual, const struct DeviceResourceNode *node)
{
struct DeviceResourceIface *drsOps = NULL;
/* 获取drsOps方法 */
drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) {
HDF_LOGE("%s: Invalid drs ops fail!", __func__);
return HDF_FAILURE;
}
/* 将配置参数依次读出,并填充至结构体中 */
if (drsOps->GetUint16(node, "busId", &virtual->busId, 0) != HDF_SUCCESS) {
HDF_LOGE("%s: Read busId fail!", __func__);
return HDF_ERR_IO;
}
if (drsOps->GetUint16(node, "busMode", &virtual->busMode, 0) != HDF_SUCCESS) {
HDF_LOGE("%s: Read busMode fail!", __func__);
return HDF_ERR_IO;
}
if (drsOps->GetUint16(node, "IrqNum", &virtual->IrqNum, 0) != HDF_SUCCESS) {
HDF_LOGE("%s: Read IrqNum fail!", __func__);
return HDF_ERR_IO;
}
···
return HDF_SUCCESS;
}
```
- Release函数参考
- Release函数开发参考
**入参:**
HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
**返回值:**
......@@ -387,7 +427,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
if (cntlr != NULL && cntlr->priv == node) {
I3cCntlrPut(cntlr);
I3cCntlrRemove(cntlr); // 【必要】主要是从管理器驱动那边移除I3cCntlr对象
virtual = (struct VirtualI3cCntlr *)cntlr;// 【必要】通过强制转换获取自定义的对象并进行release操作
virtual = (struct VirtualI3cCntlr *)cntlr; // 【必要】通过强制转换获取自定义的对象并进行release操作
(void)OsalSpinDestroy(&virtual->spin);
OsalMemFree(virtual);
}
......@@ -405,7 +445,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
return;
}
...
// 遍历、解析i3c_config.hcs中的所有配置节点,并分别进行release操作
/* 遍历、解析i3c_config.hcs中的所有配置节点,并分别进行release操作 */
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
VirtualI3cRemoveByNode(childNode); //函数定义如上
}
......@@ -441,9 +481,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、
return HDF_SUCCESS;
}
```
```c
static int32_t I3cIbiHandle(uint32_t irq, void *data)
{
struct VirtualI3cCntlr *virtual = NULL;
......
# MIPI CSI<a name="title_MIPI_CSIDes"></a>
# MIPI CSI
## 概述
## 概述<a name="section1_MIPI_CSIDes"></a>
### 功能简介
CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接口标准。CSI-2是MIPI CSI第二版,主要由应用层、协议层、物理层组成,最大支持4通道数据传输、单线传输速度高达1Gb/s。
......@@ -9,12 +10,62 @@ CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接
图1显示了简化的CSI接口。D-PHY采用1对源同步的差分时钟和1~4对差分数据线来进行数据传输。数据传输采用DDR方式,即在时钟的上下边沿都有数据传输。
**图 1** CSI发送、接收接口<a name="fig1_MIPI_CSIDes"></a>
![](figures/CSI发送-接收接口.png)
**图1** CSI发送、接收接口<a name="fig1_MIPI_CSIDes"></a>
![](figures/CSI发送-接收接口.png)
### ComboDevAttr结构体<a name="section1.1_MIPI_CSIDes"></a>
MIPI CSI标准分为应用层、协议层与物理层,协议层又细分为像素字节转换层、低级协议层、Lane管理层。
**表** **1** ComboDevAttr结构体介绍
- 物理层(PHY Layer)
PHY层指定了传输媒介,在电气层面从串行bit流中捕捉“0”与“1”,同时生成SoT与EoT等信号。
- 协议层(Protocol Layer)
协议层由三个子层组成,每个子层有不同的职责。CSI-2协议能够在host侧处理器上用一个单独的接口处理多条数据流。协议层规定了多条数据流该如何标记和交织起来,以便每条数据流能够被正确地恢复出来。
- 像素字节转换层(Pixel/Byte Packing/Unpacking Layer)
CSI-2规范支持多种不同像素格式的图像应用。在发送方中,本层在发送数据到Low Level Protocol层之前,将来自应用层的像素封包为字节数据。在接收方中,本层在发送数据到应用层之前,将来自Low Level Protocol层的字节数据解包为像素。8位的像素数据在本层中传输时保持不变。
- 低级协议层(Low Level Protocol)
LLP主要包含了在SoT和EoT事件之间的bit和byte级别的同步方法,以及和下一层传递数据的方法。LLP最小数据粒度是1个字节。LLP也包含了一个字节内的bit值解析,即Endian(大小端里的Endian的意思)的处理。
- Lane管理层(Lane Management)
CSI-2的Lane是可扩展的。具体的数据Lane的数量规范并没有给出限制,具体根据应用的带宽需求而定。发送侧分发(distributor功能)来自出口方向数据流的字节到1条或多条Lane上。接收侧则从一条或多条Lane中收集字节并合并(merge功能)到一个数据流上,复原出原始流的字节顺序。对于C-PHY物理层来说,本层专门分发字节对(16 bits)到数据Lane或从数据Lane中收集字节对。基于每Lane的扰码功能是可选特性。
协议层的数据组织形式是包(packet)。接口的发送侧会增加包头(header)和错误校验(error-checking)信息到即将被LLP发送的数据上。接收侧在LLP将包头剥掉,包头会被接收器中对应的逻辑所解析。错误校验信息可以用来做入口数据的完整性检查。
- 应用层(Application Layer)
本层描述了更高层级的应用对于数据中的数据的处理,规范并不涵盖应用层。CSI-2规范只给出了像素值和字节的映射关系。
### 运作机制
MIPI CSI模块各分层的作用为:接口层提供打开设备、写入数据和关闭设备的接口。核心层主要提供绑定设备、初始化设备以及释放设备的能力。适配层实现其它具体的功能。
![](../public_sys-resources/icon-note.gif) **说明:**<br>核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。
**图2**CSI无服务模式结构图
![image](figures/无服务模式结构图.png "CSI无服务模式结构图")
### 约束与限制
由于使用无服务模式,MIPI_CSI接口暂不支持用户态使用。
## 使用指导
### 场景介绍
MIPI CSI主要用于连接摄像头组件。
### 接口说明
MIPI CSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/mipi_csi_if.h。
**表1** ComboDevAttr结构体介绍
<a name="table1_MIPI_CSIDes"></a>
......@@ -27,47 +78,51 @@ CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接
| MIPIAttr | Mipi设备属性 |
| lvdsAttr | LVDS/SubLVDS/HiSPi设备属性 |
### ExtDataType结构体<a name="section1.2_MIPI_CSIDes"></a>
**表** **2** ExtDataType结构体介绍
**表2** ExtDataType结构体介绍
<a name="table2_MIPI_CSIDes"></a>
| 名称 | 描述 |
| --------------- | ------------------------------- |
| devno | 设备号 |
| num | sensor号 |
| num | Sensor号 |
| extDataBitWidth | 图片的位深 |
| extDataType | 定义YUV和原始数据格式以及位深度 |
### 接口说明<a name="section1.3_MIPI_CSIDes"></a>
**表 3** MIPI CSI API接口功能介绍
**表3** MIPI CSI API接口功能介绍
<a name="table3_MIPI_CSIDes"></a>
| 功能分类 | 接口名 |
| 接口名 | 接口描述 |
| -------- | -------- |
| 获取/释放MIPI CSI控制器操作句柄 | MipiCsiOpen:获取MIPI CSI控制器操作句柄<br/>MipiCsiClose:释放MIPI CSI控制器操作句柄 |
| MIPI CSI相应配置 | MipiCsiSetComboDevAttr:设置MIPI,CMOS或者LVDS相机的参数给控制器,参数包括工作模式,图像区域,图像深度,数据速率和物理通道等<br/>MipiCsiSetExtDataType(可选):设置YUV和RAW数据格式和位深<br/>MipiCsiSetHsMode:设置MIPI&nbsp;RX的Lane分布。根据硬件连接的形式选择具体的mode<br/>MipiCsiSetPhyCmvmode:设置共模电压模式 |
| 复位/撤销复位Sensor | MipiCsiResetSensor:复位Sensor<br/>MipiCsiUnresetSensor:撤销复位Sensor |
| 复位/撤销复位MIPI&nbsp;RX | MipiCsiResetRx:复位MIPI&nbsp;RX。不同的s32WorkingViNum有不同的enSnsType<br/>MipiCsiUnresetRx:撤销复位MIPI&nbsp;RX |
| 使能/关闭MIPI的时钟 | MipiCsiEnableClock:使能MIPI的时钟。根据上层函数电泳传递的enSnsType参数决定是用MIPI还是LVDS<br/>MipiCsiDisableClock:关闭MIPI设备的时钟 |
| 使能/禁用MIPI上的Sensor时钟 | MipiCsiEnableSensorClock:使能MIPI上的Sensor时钟<br/>MipiCsiDisableSensorClock:关闭Sensor的时钟 |
| DevHandle MipiCsiOpen(uint8_t id) | 获取MIPI_CSI控制器操作句柄 |
| void MipiCsiClose(DevHandle handle) | 释放MIPI_CSI控制器操作句柄 |
| int32_t MipiCsiSetComboDevAttr(DevHandle handle, ComboDevAttr \*pAttr) | 设置MIPI,CMOS或者LVDS相机的参数给控制器,参数包括工作模式,图像区域,图像深度,数据速率和物理通道等 |
| int32_t MipiCsiSetExtDataType(DevHandle handle, ExtDataType \*dataType) | 设置YUV和RAW数据格式和位深(可选) |
| int32_t MipiCsiSetHsMode(DevHandle handle, LaneDivideMode laneDivideMode) | 设置MIPI&nbsp;RX的Lane分布。根据硬件连接的形式选择具体的mode |
| int32_t MipiCsiSetPhyCmvmode(DevHandle handle, uint8_t devno, PhyCmvMode cmvMode) | 设置共模电压模式 |
| int32_t MipiCsiResetSensor(DevHandle handle, uint8_t snsResetSource) | 复位Sensor |
| int32_t MipiCsiUnresetSensor(DevHandle handle, uint8_t snsResetSource) | 撤销复位Sensor |
| int32_t MipiCsiResetRx(DevHandle handle, uint8_t comboDev) | 复位MIPI&nbsp;RX。不同的s32WorkingViNum有不同的enSnsType |
| int32_t MipiCsiUnresetRx(DevHandle handle, uint8_t comboDev) | 撤销复位MIPI&nbsp;RX |
| int32_t MipiCsiEnableClock(DevHandle handle, uint8_t comboDev) | 使能MIPI的时钟。根据上层函数电泳传递的enSnsType参数决定是用MIPI还是LVDS |
| int32_t MipiCsiDisableClock(DevHandle handle, uint8_t comboDev) | 关闭MIPI设备的时钟 |
| int32_t MipiCsiEnableSensorClock(DevHandle handle, uint8_t snsClkSource) | 使能MIPI上的Sensor时钟 |
| int32_t MipiCsiDisableSensorClock(DevHandle handle, uint8_t snsClkSource) | 关闭Sensor的时钟 |
## 使用指导<a name="section2_MIPI_CSIDes"></a>
## 开发步骤
### 使用流程<a name="section2.1_MIPI_CSIDes"></a>
#### 使用流程
使用MIPI CSI的一般流程如图2所示。
使用MIPI CSI的一般流程如图3所示。
**图 2** MIPI CSI使用流程图<a name="fig2_MIPI_CSIDes"></a>
**图3** MIPI CSI使用流程图
![](figures/MIPI-CSI使用流程图.png)
### 获取MIPI CSI控制器操作句柄<a name="section2.2_MIPI_CSIDes"></a>
#### 获取MIPI CSI控制器操作句柄
在进行MIPI CSI进行通信前,首先要调用MipiCsiOpen获取控制器操作句柄,该函数会返回指定通道ID的控制器操作句柄。
......@@ -75,9 +130,7 @@ CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接
DevHandle MipiCsiOpen(uint8_t id);
```
**表 4** MipiCsiOpen的参数和返回值描述
<a name="table4_MIPI_CSIDes"></a>
**表4** MipiCsiOpen的参数和返回值描述
| 参数 | 参数描述 |
| ---------- | ----------------------------------------------- |
......@@ -100,7 +153,7 @@ if (MipiCsiHandle == NULL) {
}
```
### MIPI CSI相应配置<a name="section2.3_MIPI_CSIDes"></a>
#### 进行MIPI CSI相应配置
- 写入MIPI CSI配置
......@@ -108,7 +161,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiSetComboDevAttr(DevHandle handle, ComboDevAttr *pAttr);
```
**表 5** MipiCsiSetComboDevAttr的参数和返回值描述
**表5** MipiCsiSetComboDevAttr的参数和返回值描述
<a name="table5_MIPI_CSIDes"></a>
......@@ -147,7 +200,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiSetExtDataType(DevHandle handle, ExtDataType* dataType);
```
**表 6** MipiCsiSetExtDataType的参数和返回值描述
**表6** MipiCsiSetExtDataType的参数和返回值描述
<a name="table6_MIPI_CSIDes"></a>
......@@ -165,7 +218,7 @@ if (MipiCsiHandle == NULL) {
/* 配置YUV和RAW数据格式和位深参数 */
dataType.devno = 0; /* 设备0 */
dataType.num = 0; /* sensor 0 */
dataType.num = 0; /* Sensor 0 */
dataType.extDataBitWidth[0] = 12; /* 位深数组元素0 */
dataType.extDataBitWidth[1] = 12; /* 位深数组元素1 */
dataType.extDataBitWidth[2] = 12; /* 位深数组元素2 */
......@@ -187,14 +240,12 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiSetHsMode(DevHandle handle, LaneDivideMode laneDivideMode);
```
**表 7** MipiCsiSetHsMode的参数和返回值描述
<a name="table7_MIPI_CSIDes"></a>
**表7** MipiCsiSetHsMode的参数和返回值描述
| 参数 | 参数描述 |
| -------------- | -------------- |
| handle | 控制器操作句柄 |
| laneDivideMode | lane模式参数 |
| laneDivideMode | Lane模式参数 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| 负数 | 设置失败 |
......@@ -203,7 +254,7 @@ if (MipiCsiHandle == NULL) {
int32_t ret;
enum LaneDivideMode mode;
/* lane模式参数为0 */
/* Lane模式参数为0 */
mode = LANE_DIVIDE_MODE_0;
/* 设置MIPI RX的 Lane分布 */
ret = MipiCsiSetHsMode(MipiCsiHandle, mode);
......@@ -219,9 +270,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiSetPhyCmvmode(DevHandle handle, uint8_t devno, PhyCmvMode cmvMode);
```
**表 8** MipiCsiSetPhyCmvmode的参数和返回值描述
<a name="table8_MIPI_CSIDes"></a>
**表8** MipiCsiSetPhyCmvmode的参数和返回值描述
| 参数 | 参数描述 |
| ---------- | ---------------- |
......@@ -249,7 +298,7 @@ if (MipiCsiHandle == NULL) {
}
```
### 复位/撤销复位Sensor<a name="section2.4_MIPI_CSIDes"></a>
#### 复位/撤销复位Sensor
- 复位Sensor
......@@ -257,9 +306,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiResetSensor(DevHandle handle, uint8_t snsResetSource);
```
**表 9** MipiCsiResetSensor的参数和返回值描述
<a name="table9_MIPI_CSIDes"></a>
**表9** MipiCsiResetSensor的参数和返回值描述
| 参数 | 参数描述 |
| -------------- | ------------------------------------------------ |
......@@ -275,7 +322,7 @@ if (MipiCsiHandle == NULL) {
/* 传感器复位信号线号为0 */
snsResetSource = 0;
/* 复位sensor */
/* 复位Sensor */
ret = MipiCsiResetSensor(MipiCsiHandle, snsResetSource);
if (ret != 0) {
HDF_LOGE("%s: MipiCsiResetSensor fail! ret=%d\n", __func__, ret);
......@@ -289,9 +336,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiUnresetSensor(DevHandle handle, uint8_t snsResetSource);
```
**表 10** MipiCsiUnresetSensor的参数和返回值描述
<a name="table10_MIPI_CSIDes"></a>
**表10** MipiCsiUnresetSensor的参数和返回值描述
| 参数 | 参数描述 |
| -------------- | ------------------------------------------------ |
......@@ -307,7 +352,7 @@ if (MipiCsiHandle == NULL) {
/* 传感器撤销复位信号线号为0 */
snsResetSource = 0;
/* 撤销复位sensor */
/* 撤销复位Sensor */
ret = MipiCsiUnresetSensor(MipiCsiHandle, snsResetSource);
if (ret != 0) {
HDF_LOGE("%s: MipiCsiUnresetSensor fail! ret=%d\n", __func__, ret);
......@@ -315,7 +360,7 @@ if (MipiCsiHandle == NULL) {
}
```
### 复位/撤销复位MIPI RX<a name="section2.5_MIPI_CSIDes"></a>
#### 复位/撤销复位MIPI RX
- 复位MIPI RX
......@@ -323,9 +368,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiResetRx(DevHandle handle, uint8_t comboDev);
```
**表 11** MipiCsiResetRx的参数和返回值描述
<a name="table11_MIPI_CSIDes"></a>
**表11** MipiCsiResetRx的参数和返回值描述
| 参数 | 参数描述 |
| ---------- | --------------------- |
......@@ -355,9 +398,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiUnresetRx(DevHandle handle, uint8_t comboDev);
```
**表 12** MipiCsiUnresetRx的参数和返回值描述
<a name="table12_MIPI_CSIDes"></a>
**表12** MipiCsiUnresetRx的参数和返回值描述
| 参数 | 参数描述 |
| ---------- | --------------------- |
......@@ -381,7 +422,7 @@ if (MipiCsiHandle == NULL) {
}
```
### 使能/关闭MIPI的时钟<a name="section2.6_MIPI_CSIDes"></a>
#### 使能/关闭MIPI的时钟
- 使能MIPI的时钟
......@@ -389,7 +430,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiEnableClock(DevHandle handle, uint8_t comboDev);
```
**表 13** MipiCsiEnableClock的参数和返回值描述
**表13** MipiCsiEnableClock的参数和返回值描述
<a name="table13_MIPI_CSIDes"></a>
......@@ -421,7 +462,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiDisableClock(DevHandle handle, uint8_t comboDev);
```
**表 14** MipiCsiDisableClock的参数和返回值描述
**表14** MipiCsiDisableClock的参数和返回值描述
<a name="table14_MIPI_CSIDes"></a>
......@@ -447,7 +488,7 @@ if (MipiCsiHandle == NULL) {
}
```
### 使能/关闭MIPI上的Sensor时钟<a name="section2.7_MIPI_CSIDes"></a>
#### 使能/关闭MIPI上的Sensor时钟<a name="section2.7_MIPI_CSIDes"></a>
- 使能MIPI上的Sensor时钟
......@@ -455,7 +496,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiEnableSensorClock(DevHandle handle, uint8_t snsClkSource);
```
**表 15** MipiCsiEnableSensorClock的参数和返回值描述
**表15** MipiCsiEnableSensorClock的参数和返回值描述
<a name="table15_MIPI_CSIDes"></a>
......@@ -473,7 +514,7 @@ if (MipiCsiHandle == NULL) {
/* 传感器的时钟信号线号为0 */
snsClkSource = 0;
/* 使能MIPI上的sensor时钟 */
/* 使能MIPI上的Sensor时钟 */
ret = MipiCsiEnableSensorClock(MipiCsiHandle, snsClkSource);
if (ret != 0) {
HDF_LOGE("%s: MipiCsiEnableSensorClock fail! ret=%d\n", __func__, ret);
......@@ -487,7 +528,7 @@ if (MipiCsiHandle == NULL) {
int32_t MipiCsiDisableSensorClock(DevHandle handle, uint8_t snsClkSource);
```
**表 16** MipiCsiDisableSensorClock的参数和返回值描述
**表16** MipiCsiDisableSensorClock的参数和返回值描述
<a name="table16_MIPI_CSIDes"></a>
......@@ -513,7 +554,7 @@ if (MipiCsiHandle == NULL) {
}
```
### 释放MIPI CSI控制器操作句柄<a name="section2.8_MIPI_CSIDes"></a>
#### 释放MIPI CSI控制器操作句柄<a name="section2.8_MIPI_CSIDes"></a>
MIPI CSI使用完成之后,需要释放控制器操作句柄,释放句柄的函数如下所示:
......@@ -523,13 +564,13 @@ void MipiCsiClose(DevHandle handle);
该函数会释放掉由MipiCsiOpen申请的资源。
**表 17** MipiCsiClose的参数和返回值描述
**表17** MipiCsiClose的参数和返回值描述
<a name="table17_MIPI_CSIDes"></a>
| 参数 | 参数描述 |
| ------------ | ------------------------------------------------ |
| handle | MIPI CSI控制器操作句柄 |
| 参数 | 参数描述 |
| ------------ | ------------------------------------------------ |
| handle | MIPI CSI控制器操作句柄 |
```c
MipiCsiClose(MIPIHandle); /* 释放掉MIPI CSI控制器操作句柄 */
......@@ -537,6 +578,8 @@ MipiCsiClose(MIPIHandle); /* 释放掉MIPI CSI控制器操作句柄 */
## 使用实例<a name="section3_MIPI_CSIDes"></a>
本例拟对Hi3516DV300开发板上MIPI CSI设备进行操作。
MIPI CSI完整的使用示例如下所示:
```c
......@@ -565,7 +608,7 @@ void PalMipiCsiTestSample(void)
return;
}
/* lane模式参数为0 */
/* Lane模式参数为0 */
mode = LANE_DIVIDE_MODE_0;
/* 设置MIPI RX的Lane分布 */
ret = MipiCsiSetHsMode(MipiCsiHandle, mode);
......@@ -592,14 +635,14 @@ void PalMipiCsiTestSample(void)
/* 传感器的时钟信号线号为0 */
snsClkSource = 0;
/* 使能MIPI上的sensor时钟 */
/* 使能MIPI上的Sensor时钟 */
ret = MipiCsiEnableSensorClock(MipiCsiHandle, snsClkSource);
if (ret != 0) {
HDF_LOGE("%s: MipiCsiEnableSensorClock fail! ret=%d\n", __func__, ret);
return;
}
/* 复位sensor */
/* 复位Sensor */
ret = MipiCsiResetSensor(MipiCsiHandle, snsResetSource);
if (ret != 0) {
HDF_LOGE("%s: MipiCsiResetSensor fail! ret=%d\n", __func__, ret);
......@@ -651,14 +694,14 @@ void PalMipiCsiTestSample(void)
/* 传感器撤销复位信号线号为0 */
snsResetSource = 0;
/* 撤销复位sensor */
/* 撤销复位Sensor */
ret = MipiCsiUnresetSensor(MipiCsiHandle, snsResetSource);
if (ret != 0) {
HDF_LOGE("%s: MipiCsiUnresetSensor fail! ret=%d\n", __func__, ret);
return;
}
/* 关闭MIPI上的sensor时钟 */
/* 关闭MIPI上的Sensor时钟 */
ret = MipiCsiDisableSensorClock(MipiCsiHandle, snsClkSource);
if (ret != 0) {
HDF_LOGE("%s: MipiCsiDisableSensorClock fail! ret=%d\n", __func__, ret);
......
......@@ -2,13 +2,66 @@
## 概述
CSI(Camera Serial Interface)是由MIPI(Mobile Industry Processor Interface)联盟下Camera工作组指定的接口标准。在HDF框架中,MIPI CSI的接口适配模式采用无服务模式,用于不需要在用户态提供API的设备类型,或者没有用户态和内核区分的OS系统,MIPI CSI的接口关联方式是DevHandle直接指向设备对象内核态地址(DevHandle是一个void类型指针)。
### 功能简介
图 1 无服务模式结构图
CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接口标准。CSI-2是MIPI CSI第二版,主要由应用层、协议层、物理层组成,最大支持4通道数据传输、单线传输速度高达1Gb/s。
物理层支持HS(High Speed)和LP(Low Speed)两种工作模式。HS模式下采用低压差分信号,功耗较大,但数据传输速率可以很高(数据速率为80M~1Gbps);LP模式下采用单端信号,数据速率很低(<10Mbps),但是相应的功耗也很低。两种模式的结合保证了MIPI总线在需要传输大量数据(如图像)时可以高速传输,而在不需要传输大数据量时又能够减少功耗。
图1显示了简化的CSI接口。D-PHY采用1对源同步的差分时钟和1~4对差分数据线来进行数据传输。数据传输采用DDR方式,即在时钟的上下边沿都有数据传输。
**图 1** CSI发送、接收接口
![](figures/CSI发送-接收接口.png)
MIPI CSI标准分为应用层、协议层与物理层,协议层又细分为像素字节转换层、低级协议层、Lane管理层。
- 物理层(PHY Layer)
PHY层指定了传输媒介,在电气层面从串行bit流中捕捉“0”与“1”,同时生成SoT与EoT等信号。
- 协议层(Protocol Layer)
协议层由三个子层组成,每个子层有不同的职责。CSI-2协议能够在host侧处理器上用一个单独的接口处理多条数据流。协议层规定了多条数据流该如何标记和交织起来,以便每条数据流能够被正确地恢复出来。
- 像素字节转换层(Pixel/Byte Packing/Unpacking Layer)
CSI-2规范支持多种不同像素格式的图像应用。在发送方中,本层在发送数据到Low Level Protocol层之前,将来自应用层的像素封包为字节数据。在接收方中,本层在发送数据到应用层之前,将来自Low Level Protocol层的字节数据解包为像素。8位的像素数据在本层中传输时保持不变。
- 低级协议层(Low Level Protocol)
LLP主要包含了在SoT和EoT事件之间的bit和byte级别的同步方法,以及和下一层传递数据的方法。LLP最小数据粒度是1个字节。LLP也包含了一个字节内的bit值解析,即Endian(大小端里的Endian的意思)的处理。
- Lane管理层(Lane Management)
CSI-2的Lane是可扩展的。具体的数据Lane的数量规范并没有给出限制,具体根据应用的带宽需求而定。发送侧分发(distributor功能)来自出口方向数据流的字节到1条或多条Lane上。接收侧则从一条或多条Lane中收集字节并合并(merge功能)到一个数据流上,复原出原始流的字节顺序。对于C-PHY物理层来说,本层专门分发字节对(16 bits)到数据Lane或从数据Lane中收集字节对。基于每Lane的扰码功能是可选特性。
协议层的数据组织形式是包(packet)。接口的发送侧会增加包头(header)和错误校验(error-checking)信息到即将被LLP发送的数据上。接收侧在LLP将包头剥掉,包头会被接收器中对应的逻辑所解析。错误校验信息可以用来做入口数据的完整性检查。
- 应用层(Application Layer)
本层描述了更高层级的应用对于数据中的数据的处理,规范并不涵盖应用层。CSI-2规范只给出了像素值和字节的映射关系。
### 运作机制
MIPI CSI模块各分层的作用为:
- 接口层提供打开设备、写入数据和关闭设备的接口。
- 核心层主要提供绑定设备、初始化设备以及释放设备的能力。
- 适配层实现其它具体的功能。
![](../public_sys-resources/icon-note.gif) **说明:**<br>核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。
![image1](figures/无服务模式结构图.png)
## 接口说明
## 开发指导
### 场景介绍
MIPI CSI仅是一个软件层面的概念,主要工作是CSI资源管理。开发者可以通过使用提供的CSI操作接口,实现对CSI资源管理。当驱动开发者需要将MIPI CSI设备适配到OpenHarmony时,需要进行MIPI CSI驱动适配,下文将介绍如何进行MIPI CSI驱动适配。
### 接口说明
为了保证上层在调用MIPI CSI接口时能够正确的操作硬件,核心层在//drivers/hdf_core/framework/support/platform/include/mipi/mipi_csi_core.h中定义了以下钩子函数。驱动适配者需要在适配层实现这些函数的具体功能,并与这些钩子函数挂接,从而完成接口层与核心层的交互。
MipiCsiCntlrMethod定义:
......@@ -28,13 +81,13 @@ struct MipiCsiCntlrMethod {
int32_t (*unresetSensor)(struct MipiCsiCntlr *cntlr, uint8_t snsResetSource);
};
```
表1 MipiCsiCntlrMethod成员的回调函数功能说明
**表1** MipiCsiCntlrMethod成员的钩子函数功能说明
| 成员函数 | 入参 | 出参 | 返回状态 | 功能 |
| ------------------ | ------------------------------------------------------------ | ---- | ------------------ | -------------------------- |
| setComboDevAttr | **cntlr**:结构体指针,MipiCsi控制器 ;<br>**pAttr**:结构体指针,MIPI CSI相应配置结构体指针 | 无 | HDF_STATUS相关状态 | 写入MIPI CSI配置 |
| setPhyCmvmode | **cntlr**:结构体指针,MipiCsi控制器 ;<br>**devno**:uint8_t,设备编号;<br>**cmvMode**:枚举类型,共模电压模式参数 | 无 | HDF_STATUS相关状态 | 设置共模电压模式 |
| setExtDataType | **cntlr**:结构体指针,MipiCsi控制器 ;<br>**dataType**:结构体指针,定义YUV和原始数据格式以及位深度 | 无 | HDF_STATUS相关状态 | 设置YUV和RAW数据格式和位深 |
| setHsMode | **cntlr**:结构体指针,MipiCsi控制器 ;<br>**laneDivideMode**:枚举类型,lane模式参数 | 无 | HDF_STATUS相关状态 | 设置MIPI RX的Lane分布 |
| setHsMode | **cntlr**:结构体指针,MipiCsi控制器 ;<br>**laneDivideMode**:枚举类型,Lane模式参数 | 无 | HDF_STATUS相关状态 | 设置MIPI RX的Lane分布 |
| enableClock | **cntlr**:结构体指针,MipiCsi控制器 ;<br>**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 使能MIPI的时钟 |
| disableClock | **cntlr**:结构体指针,MipiCsi控制器 ;<br/>**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 关闭MIPI的时钟 |
| resetRx | **cntlr**:结构体指针,MipiCsi控制器 ;<br/>**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 复位MIPI RX |
......@@ -44,7 +97,7 @@ struct MipiCsiCntlrMethod {
| resetSensor | **cntlr**:结构体指针,MipiCsi控制器 ;<br/>**snsClkSource**:uint8_t,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 复位Sensor |
| unresetSensor | **cntlr**:结构体指针,MipiCsi控制器 ;<br/>**snsClkSource**:uint8_t,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 撤销复位Sensor |
## 开发步骤
### 开发步骤
MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动入口、以及实例化核心层接口函数。
......@@ -70,20 +123,19 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
## 开发实例
### 开发实例
下方将以mipi_rx_hi35xx.c为示例,展示需要厂商提供哪些内容来完整实现设备功能。
1. 一般来说,驱动开发首先需要在busxx_config.hcs中配置器件属性,并在device_info.hcs文件中添加deviceNode描述
1. 一般来说,驱动开发首先需要新增mipicsi_config.hcs配置文件,在其中配置器件属性,并在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。deviceNode与配置属性的对应关系是依靠deviceMatchAttr字段来完成的。只有当deviceNode下的deviceMatchAttr字段与配置属性文件中的match_attr字段完全相同时,驱动才能正确读取配置数据
器件属性值与核心层MipiCsiCntlr 成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。
>![](../public_sys-resources/icon-note.gif) **说明:**<br>
>本例中MIPI控制器自身属性在源文件文件中,如有厂商需要,则在device_info文件的deviceNode增加deviceMatchAttr信息,相应增加mipicsi_config.hcs文件。
>![icon-note.gif](../public_sys-resources/icon-note.gif) **说明:**<br>
>本例中MIPI控制器配置属性在源文件中,没有新增配置文件,驱动适配者如有需要,可在device_info.hcs文件的deviceNode增加deviceMatchAttr字段,同时新增mipicsi_config.hcs文件,并使其match_attr字段与之相同。
- device_info.hcs 配置参考
device_info.hcs配置参考
```c
root {
......@@ -108,17 +160,17 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
2. 完成器件属性文件的配置之后,下一步请实例化驱动入口。
驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员会被厂商操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。
驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员需要被驱动适配者操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。
一般在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
- MIPI CSI驱动入口参考
MIPI CSI驱动入口参考
```c
struct HdfDriverEntry g_mipiCsiDriverEntry = {
.moduleVersion = 1,
.Init = Hi35xxMipiCsiInit, // 见Init参考
.Release = Hi35xxMipiCsiRelease, // 见Release参考
.Init = Hi35xxMipiCsiInit, // 见Init开发参考
.Release = Hi35xxMipiCsiRelease, // 见Release开发参考
.moduleName = "HDF_MIPI_RX", // 【必要】需要与device_info.hcs 中保持一致
};
HDF_INIT(g_mipiCsiDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
......@@ -126,9 +178,9 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
3. 完成驱动入口注册之后,最后一步就是以核心层MipiCsiCntlr对象的初始化为核心,实现HdfDriverEntry成员函数(Bind,Init,Release)。
MipiCsiCntlr对象的初始化包括厂商自定义结构体(用于传递参数和数据)和实例化MipiCsiCntlr成员MipiCsiCntlrMethod(让用户可以通过接口来调用驱动底层函数)。
MipiCsiCntlr对象的初始化包括驱动适配者自定义结构体(用于传递参数和数据)和实例化MipiCsiCntlr成员MipiCsiCntlrMethod(让用户可以通过接口来调用驱动底层函数)。
- 自定义结构体参考
- 自定义结构体参考
从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,本例的mipicsi器件属性在源文件中,故基本成员结构与MipiCsiCntlr无太大差异。
......@@ -162,7 +214,7 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
};
} ComboDevAttr;
// MipiCsiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
/* MipiCsiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */
struct MipiCsiCntlr {
/** 当驱动程序绑定到HDF框架时,将发送此控制器提供的服务。 */
struct IDeviceIoService service;
......@@ -185,13 +237,11 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
};
```
- MipiCsiCntlr成员回调函数结构体MipiCsiCntlrMethod的实例化
- MipiCsiCntlr成员钩子函数结构体MipiCsiCntlrMethod的实例化
>![](../public_sys-resources/icon-note.gif) **说明:**<br>
>其他成员在Init函数中初始化。
```c
static struct MipiCsiCntlrMethod g_method = {
.setComboDevAttr = Hi35xxSetComboDevAttr,
......@@ -209,16 +259,17 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
};
```
- **Init函数参考**
- Init函数开发参考
**入参:**
入参:
HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
**返回值:**
返回值:
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
**表2** HDF_STATUS返回值描述
| 状态(值) | 问题描述 |
| :--------------------- | :----------: |
......@@ -229,10 +280,9 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
| HDF_SUCCESS | 执行成功 |
| HDF_FAILURE | 执行失败 |
**函数说明:**
MipiCsiCntlrMethod的实例化对象的挂载,调用MipiCsiRegisterCntlr,以及其他厂商自定义初始化操作。
函数说明:
MipiCsiCntlrMethod的实例化对象的挂载,调用MipiCsiRegisterCntlr,以及其他驱动适配者自定义初始化操作。
```c
static int32_t Hi35xxMipiCsiInit(struct HdfDeviceObject *device)
......@@ -243,8 +293,8 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
g_mipiCsi.priv = NULL; // g_mipiTx是定义的全局变量
// static struct MipiCsiCntlr g_mipiCsi = {
// .devNo = 0
//};
g_mipiCsi.ops = &g_method; //MipiCsiCntlrMethod的实例化对象的挂载
// };
g_mipiCsi.ops = &g_method; // MipiCsiCntlrMethod的实例化对象的挂载
#ifdef CONFIG_HI_PROC_SHOW_SUPPORT
g_mipiCsi.debugs = &g_debugMethod;
#endif
......@@ -254,7 +304,7 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
return ret;
}
ret = MipiRxDrvInit(); // 【必要】厂商对设备的初始化,形式不限。
ret = MipiRxDrvInit(); // 【必要】驱动适配者对设备的初始化,形式不限。
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: [MipiRxDrvInit] failed.", __func__);
return ret;
......@@ -273,11 +323,11 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
return ret;
}
// mipi_csi_core.c核心层
/* mipi_csi_core.c核心层 */
int32_t MipiCsiRegisterCntlr(struct MipiCsiCntlr *cntlr, struct HdfDeviceObject *device)
{
...
// 定义的全局变量:static struct MipiCsiHandle g_mipiCsihandle[MAX_CNTLR_CNT];
/* 定义的全局变量:static struct MipiCsiHandle g_mipiCsihandle[MAX_CNTLR_CNT]; */
if (g_mipiCsihandle[cntlr->devNo].cntlr == NULL) {
(void)OsalMutexInit(&g_mipiCsihandle[cntlr->devNo].lock);
(void)OsalMutexInit(&(cntlr->lock));
......@@ -297,24 +347,23 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
}
```
- **Release函数参考**
- Release函数开发参考
**入参:**
入参:
HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
**返回值:**
返回值:
**函数说明:**
函数说明:
该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。
>![](../public_sys-resources/icon-note.gif) **说明:**<br>
>![icon-note.gif](../public_sys-resources/icon-note.gif) **说明:**<br>
>所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
```c
static void Hi35xxMipiCsiRelease(struct HdfDeviceObject *device)
{
......@@ -328,11 +377,10 @@ MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动
#ifdef MIPICSI_VFS_SUPPORT
MipiCsiDevModuleExit(cntlr->devNo);
#endif
MipiRxDrvExit(); // 【必要】对厂商设备所占资源的释放
MipiRxDrvExit(); // 【必要】对设备所占资源的释放
MipiCsiUnregisterCntlr(&g_mipiCsi); // 空函数
g_mipiCsi.priv = NULL;
HDF_LOGI("%s: unload mipi csi driver success!", __func__);
}
```
# MIPI DSI
## 概述
### 功能简介
DSI(Display Serial Interface)是由移动行业处理器接口联盟(Mobile Industry Processor Interface (MIPI) Alliance)制定的规范,旨在降低移动设备中显示控制器的成本。它以串行的方式发送像素数据或指令给外设(通常是LCD或者类似的显示设备),或从外设中读取状态信息或像素信息;它定义了主机、图像数据源和目标设备之间的串行总线和通信协议。
MIPI DSI具备高速模式和低速模式两种工作模式,全部数据通道都可以用于单向的高速传输,但只有第一个数据通道才可用于低速双向传输,从属端的状态信息、像素等是通过该数据通道返回。时钟通道专用于在高速传输数据的过程中传输同步时钟信号。
图1显示了简化的DSI接口。从概念上看,符合DSI的接口与基于DBI-2和DPI-2标准的接口具有相同的功能。它向外围设备传输像素或命令数据,并且可以从外围设备读取状态或像素信息。主要区别在于,DSI对所有像素数据、命令和事件进行序列化,而在传统接口中,这些像素数据、命令和事件通常需要附加控制信号才能在并行数据总线上传输。
**图1** DSI发送、接收接口
**图1** DSI发送、接收接口
![image](figures/DSI发送-接收接口.png "DSI发送-接收接口")
![image](figures/DSI发送-接收接口.png "DSI发送-接收接口")
DSI标准对应D-PHY、DSI、DCS规范,可分为四层:
## 接口说明
- PHY Layer
**表1** MIPI DSI API接口功能介绍
定义了传输媒介,输入/输出电路和和时钟和信号机制。PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。
| 功能分类 | 接口名 |
| -------- | -------- |
| 设置/获取当前MIPI&nbsp;DSI相关配置 | -&nbsp;MipiDsiSetCfg:设置MIPI&nbsp;DSI相关配置<br/>-&nbsp;MipiDsiGetCfg:获取当前MIPI&nbsp;DSI相关配置 |
| 获取/释放MIPI&nbsp;DSI操作句柄 | -&nbsp;MipiDsiOpen:获取MIPI&nbsp;DSI操作句柄<br/>-&nbsp;MipiDsiClose:释放MIPI&nbsp;DSI操作句柄 |
| 设置MIPI&nbsp;DSI进入Low&nbsp;power模式/High&nbsp;speed模式 | -&nbsp;MipiDsiSetLpMode:设置MIPI&nbsp;DSI进入Low&nbsp;power模式<br/>-&nbsp;MipiDsiSetHsMode:设置MIPI&nbsp;DSI进入High&nbsp;speed模式 |
| MIPI&nbsp;DSI发送/回读指令 | -&nbsp;MipiDsiTx:MIPI&nbsp;DSI发送相应指令的接口<br/>-&nbsp;MipiDsiRx:MIPI&nbsp;DSI按期望长度回读的接口 |
- Lane Management层
负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode,High-Speed(Burst) mode,Control mode。
- Low Level Protocol层
定义了如何组帧和解析以及错误检测等。
- Application层
描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。
### 运作机制
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
> 本文涉及的所有接口,仅限内核态使用,不支持在用户态使用。
MIPI DSI软件模块各分层的作用为:
- 接口层:提供打开设备、写入数据和关闭设备的接口。
- 核心层:主要提供绑定设备、初始化设备以及释放设备的能力。
- 适配层:实现其它具体的功能。
![](../public_sys-resources/icon-note.gif) **说明:**<br>核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。
**图2** DSI无服务模式结构图
![image](figures/无服务模式结构图.png "DSI无服务模式结构图")
### 约束与限制
由于使用无服务模式,MIPI_DSI接口暂不支持用户态使用。
## 使用指导
### 场景介绍
### 使用流程
MIPI DSI主要用于连接显示屏。
### 接口说明
MIPI DSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/mipi_dsi_if.h。
**表1** MIPI DSI API接口功能介绍
| 功能分类 | 接口名 |
| -------- | -------- |
| DevHandle MipiDsiOpen(uint8_t id) | 获取MIPI&nbsp;DSI操作句柄 |
| void MipiDsiClose(DevHandle handle) | 释放MIPI&nbsp;DSI操作句柄 |
| int32_t MipiDsiSetCfg(DevHandle handle, struct MipiCfg \*cfg) | 设置MIPI&nbsp;DSI相关配置 |
| int32_t MipiDsiGetCfg(DevHandle handle, struct MipiCfg \*cfg) | 获取当前MIPI&nbsp;DSI相关配置 |
| void MipiDsiSetLpMode(DevHandle handle) | 设置MIPI&nbsp;DSI进入Low&nbsp;power模式 |
| void MipiDsiSetHsMode(DevHandle handle) | 设置MIPI&nbsp;DSI进入High&nbsp;speed模式 |
| int32_t MipiDsiTx(DevHandle handle, struct DsiCmdDesc \*cmd) | DSI发送指令 |
| int32_t MipiDsiRx(DevHandle handle, struct DsiCmdDesc \*cmd, int32_t readLen, uint8_t \*out) | MIPI&nbsp;DSI按期望长度回读数据 |
### 开发步骤
使用MIPI DSI的一般流程如下图所示。
**图2** MIPI DSI使用流程图
**图3** MIPI DSI使用流程图
![image](figures/MIPI-DSI使用流程图.png "MIPI-DSI使用流程图")
![image](figures/MIPI-DSI使用流程图.png "MIPI-DSI使用流程图")
### 获取MIPI DSI操作句柄
#### 获取MIPI DSI操作句柄
在进行MIPI DSI进行通信前,首先要调用MipiDsiOpen获取操作句柄,该函数会返回指定通道ID的操作句柄。
```
```c
DevHandle MipiDsiOpen(uint8_t id);
```
......@@ -62,7 +103,7 @@ DevHandle MipiDsiOpen(uint8_t id);
假设系统中的MIPI DSI通道为0,获取该通道操作句柄的示例如下:
```
```c
DevHandle mipiDsiHandle = NULL; /* 设备句柄 */
chnId = 0; /* MIPI DSI通道ID */
......@@ -75,11 +116,11 @@ if (mipiDsiHandle == NULL) {
```
### MIPI DSI相应配置
#### MIPI DSI相应配置
- 写入MIPI DSI配置
```
```c
int32_t MipiDsiSetCfg(DevHandle handle, struct MipiCfg *cfg);
```
......@@ -94,7 +135,7 @@ if (mipiDsiHandle == NULL) {
| 负数 | 设置失败 |
```
```c
int32_t ret;
struct MipiCfg cfg = {0};
......@@ -123,7 +164,7 @@ if (mipiDsiHandle == NULL) {
- 获取当前MIPI DSI的配置
```
```c
int32_t MipiDsiGetCfg(DevHandle handle, struct MipiCfg *cfg);
```
......@@ -138,7 +179,7 @@ if (mipiDsiHandle == NULL) {
| 负数 | 获取失败 |
```
```c
int32_t ret;
struct MipiCfg cfg;
memset(&cfg, 0, sizeof(struct MipiCfg));
......@@ -150,11 +191,11 @@ if (mipiDsiHandle == NULL) {
```
### 发送/回读控制指令
#### 发送/回读控制指令
- 发送指令
```
```c
int32_t MipiDsiTx(PalHandle handle, struct DsiCmdDesc *cmd);
```
......@@ -169,7 +210,7 @@ if (mipiDsiHandle == NULL) {
| 负数 | 发送失败 |
```
```c
int32_t ret;
struct DsiCmdDesc *cmd = OsalMemCalloc(sizeof(struct DsiCmdDesc));
if (cmd == NULL) {
......@@ -198,7 +239,7 @@ if (mipiDsiHandle == NULL) {
- 回读指令
```
```c
int32_t MipiDsiRx(DevHandle handle, struct DsiCmdDesc *cmd, uint32_t readLen, uint8_t *out);
```
......@@ -215,7 +256,7 @@ if (mipiDsiHandle == NULL) {
| 负数 | 获取失败 |
```
```c
int32_t ret;
uint8_t readVal = 0;
......@@ -245,12 +286,11 @@ if (mipiDsiHandle == NULL) {
```
### 释放MIPI DSI操作句柄
#### 释放MIPI DSI操作句柄
MIPI DSI使用完成之后,需要释放操作句柄,释放句柄的函数如下所示:
```
```c
void MipiDsiClose(DevHandle handle);
```
......@@ -262,18 +302,18 @@ void MipiDsiClose(DevHandle handle);
| -------- | -------- |
| handle | MIPI&nbsp;DSI操作句柄 |
```
```c
MipiDsiClose(mipiHandle); /* 释放掉MIPI DSI操作句柄 */
```
## 使用实例
MIPI DSI完整的使用示例如下所示:
本例拟对Hi3516DV300开发板上MIPI DSI设备进行操作。
MIPI DSI完整的使用示例如下所示:
```
```c
#include "hdf.h"
#include "mipi_dsi_if.h"
......
# MIPI DSI
## 概述
DSI(Display Serial Interface)是由移动行业处理器接口联盟(Mobile Industry Processor Interface (MIPI) Alliance)制定的规范。在HDF框架中,MIPI DSI的接口适配模式采用无服务模式,用于不需要在用户态提供API的设备类型,或者没有用户态和内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址(DevHandle是一个void类型指针)。
### 功能简介
DSI(Display Serial Interface)是由移动行业处理器接口联盟(Mobile Industry Processor Interface (MIPI) Alliance)制定的规范,旨在降低移动设备中显示控制器的成本。它以串行的方式发送像素数据或指令给外设(通常是LCD或者类似的显示设备),或从外设中读取状态信息或像素信息;它定义了主机、图像数据源和目标设备之间的串行总线和通信协议。
MIPI DSI具备高速模式和低速模式两种工作模式,全部数据通道都可以用于单向的高速传输,但只有第一个数据通道才可用于低速双向传输,从属端的状态信息、像素等是通过该数据通道返回。时钟通道专用于在高速传输数据的过程中传输同步时钟信号。
图1显示了简化的DSI接口。从概念上看,符合DSI的接口与基于DBI-2和DPI-2标准的接口具有相同的功能。它向外围设备传输像素或命令数据,并且可以从外围设备读取状态或像素信息。主要区别在于,DSI对所有像素数据、命令和事件进行序列化,而在传统接口中,这些像素数据、命令和事件通常需要附加控制信号才能在并行数据总线上传输。
**图1** DSI发送、接收接口
![image](figures/DSI发送-接收接口.png "DSI发送-接收接口")
DSI标准对应D-PHY、DSI、DCS规范,可分为四层:
- PHY Layer
定义了传输媒介,输入/输出电路和和时钟和信号机制。PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。
- Lane Management层
负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode, High-Speed(Burst) mode, Control mode 。
- Low Level Protocol层
定义了如何组帧和解析以及错误检测等。
- Application层
描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。
### 运作机制
**图1** DSI无服务模式结构图
MIPI DSI软件模块各分层的作用为:
- 接口层:提供打开设备、写入数据和关闭设备的接口。
- 核心层:主要提供绑定设备、初始化设备以及释放设备的能力。
- 适配层:实现其它具体的功能。
![](../public_sys-resources/icon-note.gif) **说明:**<br>核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。
**图2** DSI无服务模式结构图
![image](figures/无服务模式结构图.png "DSI无服务模式结构图")
## 开发指导
## 接口说明
### 场景介绍
MipiDsiCntlrMethod定义:
MIPI DSI仅是一个软件层面的概念,主要工作是MIPI DSI资源管理。开发者可以通过使用提供的提供的操作接口,实现DSI资源管理。当驱动开发者需要将MIPI DSI设备适配到OpenHarmony时,需要进行MIPI DSI驱动适配,下文将介绍如何进行MIPI DSI驱动适配。
### 接口说明
```
为了保证上层在调用MIPI DSI接口时能够正确的操作硬件,核心层在//drivers/hdf_core/framework/support/platform/include/mipi/mipi_dsi_core.h中定义了以下钩子函数。驱动适配者需要在适配层实现这些函数的具体功能,并与这些钩子函数挂接,从而完成接口层与核心层的交互。
MipiDsiCntlrMethod定义:
```c
struct MipiDsiCntlrMethod { // 核心层结构体的成员函数
int32_t (*setCntlrCfg)(struct MipiDsiCntlr *cntlr);
int32_t (*setCmd)(struct MipiDsiCntlr *cntlr, struct DsiCmdDesc *cmd);
......@@ -24,12 +68,12 @@ struct MipiDsiCntlrMethod { // 核心层结构体的成员函数
void (*toLp)(struct MipiDsiCntlr *cntlr);
void (*enterUlps)(struct MipiDsiCntlr *cntlr); //【可选】进入超低功耗模式
void (*exitUlps)(struct MipiDsiCntlr *cntlr); //【可选】退出超低功耗模式
int32_t (*powerControl)(struct MipiDsiCntlr *cntlr, uint8_t enable);//【可选】使能/去使能功耗控制
int32_t (*powerControl)(struct MipiDsiCntlr *cntlr, uint8_t enable); //【可选】使能/去使能功耗控制
int32_t (*attach)(struct MipiDsiCntlr *cntlr); //【可选】将一个DSI设备连接上host
};
```
**表1** MipiDsiCntlrMethod成员的回调函数功能说明
**表1** MipiDsiCntlrMethod成员的钩子函数功能说明
| 成员函数 | 入参 | 出参 | 返回状态 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
......@@ -40,7 +84,7 @@ struct MipiDsiCntlrMethod { // 核心层结构体的成员函数
| toLp | cntlr:结构体指针,MipiDsi控制器 | 无 | HDF_STATUS相关状态 | 设置为低电模式 |
## 开发步骤
### 开发步骤
MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动入口,以及实例化核心层接口函数。
......@@ -63,19 +107,19 @@ MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
## 开发实例
### 开发实例
下方将以mipi_tx_hi35xx.c为示例,展示需要厂商提供哪些内容来完整实现设备功能。
下方将以mipi_tx_hi35xx.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。
1. 一般来说,驱动开发首先需要在xx_config.hcs中配置器件属性,并在device_info.hcs文件中添加deviceNode描述
1. 一般来说,驱动开发首先需要mipicsi_config.hcs配置文件,在其中配置器件属性,并在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。deviceNode与配置属性的对应关系是依靠deviceMatchAttr字段来完成的。只有当deviceNode下的deviceMatchAttr字段与配置属性文件中的match_attr字段完全相同时,驱动才能正确读取配置数据
器件属性值与核心层MipiDsiCntlr成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。
但本例中MIPI控制器无需配置额外属性,如有厂商需要,则需要在device_info文件的deviceNode增加deviceMatchAttr信息,以及增加mipidsi_config文件。
但本例中MIPI控制器无需配置额外属性,驱动适配者如有需要,则需要在device_info.hcs文件的deviceNode增加deviceMatchAttr信息,以及增加mipidsi_config.hcs文件。
device_info.hcs 配置参考:
```
```c
root {
device_info {
match_attr = "hdf_manager";
......@@ -98,33 +142,33 @@ MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动
2. 完成器件属性文件的配置之后,下一步请实例化驱动入口。
驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员会被厂商操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。
驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员需要被驱动适配者操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。
一般在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
MIPI DSI驱动入口参考:
```
```c
struct HdfDriverEntry g_mipiTxDriverEntry = {
.moduleVersion = 1,
.Init = Hi35xxMipiTxInit, // 见Init参考
.Release = Hi35xxMipiTxRelease,// 见Release参考
.Init = Hi35xxMipiTxInit, // 见Init开发参考
.Release = Hi35xxMipiTxRelease, // 见Release开发参考
.moduleName = "HDF_MIPI_TX", // 【必要】需要与device_info.hcs 中保持一致。
};
HDF_INIT(g_mipiTxDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中
```
3. 完成驱动入口注册之后,下一步就是以核心层MipiDsiCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化MipiDsiCntlr成员MipiDsiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
3. 完成驱动入口注册之后,下一步就是以核心层MipiDsiCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化MipiDsiCntlr成员MipiDsiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
- 自定义结构体参考
从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,但本例的mipidsi无器件属性文件,故基本成员结构与MipiDsiCntlr无太大差异。
```
```c
typedef struct {
unsigned int devno; // 设备号
short laneId[LANE_MAX_NUM]; // lane号
short laneId[LANE_MAX_NUM]; // Lane号
OutPutModeTag outputMode; // 输出模式选择:刷新模式,命令行模式或视频流模式
VideoModeTag videoMode; // 显示设备的同步模式
OutputFormatTag outputFormat; // 输出DSI图像数据格式:RGB或YUV
......@@ -133,7 +177,7 @@ MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动
unsigned int pixelClk; // 时钟,单位KHz
} ComboDevCfgTag;
// MipiDsiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。
/* MipiDsiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */
struct MipiDsiCntlr {
struct IDeviceIoService service;
struct HdfDeviceObject *device;
......@@ -144,10 +188,10 @@ MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动
void *priv;
};
```
- MipiDsiCntlr成员回调函数结构体MipiDsiCntlrMethod的实例化,其他成员在Init函数中初始化。
- MipiDsiCntlr成员钩子函数结构体MipiDsiCntlrMethod的实例化,其他成员在Init函数中初始化。
```
```c
static struct MipiDsiCntlrMethod g_method = {
.setCntlrCfg = Hi35xxSetCntlrCfg,
.setCmd = Hi35xxSetCmd,
......@@ -156,7 +200,7 @@ MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动
.toLp = Hi35xxToLp,
};
```
- Init函数参考
- Init函数开发参考
入参:
......@@ -164,8 +208,9 @@ MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动
返回值:
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
**表2** HDF_STATUS返回值描述
| 状态(值) | 问题描述 |
| -------- | -------- |
......@@ -178,10 +223,10 @@ MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动
函数说明:
MipiDsiCntlrMethod的实例化对象的挂载,调用MipiDsiRegisterCntlr,以及其他厂商自定义初始化操作。
MipiDsiCntlrMethod的实例化对象的挂载,调用MipiDsiRegisterCntlr,以及其他驱动适配者自定义初始化操作。
```
```c
static int32_t Hi35xxMipiTxInit(struct HdfDeviceObject *device)
{
int32_t ret;
......@@ -190,21 +235,21 @@ MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动
// .devNo=0
//};
g_mipiTx.ops = &g_method; // MipiDsiCntlrMethod的实例化对象的挂载
ret = MipiDsiRegisterCntlr(&g_mipiTx, device);// 【必要】调用核心层函数和g_mipiTx初始化核心层全局变量
ret = MipiDsiRegisterCntlr(&g_mipiTx, device); // 【必要】调用核心层函数和g_mipiTx初始化核心层全局变量
...
return MipiTxDrvInit(0); // 【必要】厂商对设备的初始化,形式不限
return MipiTxDrvInit(0); // 【必要】驱动适配者对设备的初始化,形式不限
}
// mipi_dsi_core.c核心层
/* mipi_dsi_core.c核心层 */
int32_t MipiDsiRegisterCntlr(struct MipiDsiCntlr *cntlr, struct HdfDeviceObject *device)
{
...
// 定义的全局变量:static struct MipiDsiHandle g_mipiDsihandle[MAX_CNTLR_CNT];
/* 定义的全局变量:static struct MipiDsiHandle g_mipiDsihandle[MAX_CNTLR_CNT]; */
if (g_mipiDsihandle[cntlr->devNo].cntlr == NULL) {
(void)OsalMutexInit(&g_mipiDsihandle[cntlr->devNo].lock);
(void)OsalMutexInit(&(cntlr->lock));
g_mipiDsihandle[cntlr->devNo].cntlr = cntlr;// 初始化MipiDsiHandle成员
g_mipiDsihandle[cntlr->devNo].cntlr = cntlr; // 初始化MipiDsiHandle成员
g_mipiDsihandle[cntlr->devNo].priv = NULL;
cntlr->device = device; // 使HdfDeviceObject与MipiDsiHandle可以相互转化的前提
device->service = &(cntlr->service); // 使HdfDeviceObject与MipiDsiHandle可以相互转化的前提
......@@ -216,7 +261,7 @@ MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动
return HDF_FAILURE;
}
```
- Release函数参考
- Release函数开发参考
入参:
......@@ -233,15 +278,15 @@ MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
> 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。
```
```c
static void Hi35xxMipiTxRelease(struct HdfDeviceObject *device)
{
struct MipiDsiCntlr *cntlr = NULL;
...
cntlr = MipiDsiCntlrFromDevice(device);// 这里有HdfDeviceObject到MipiDsiCntlr的强制转化
cntlr = MipiDsiCntlrFromDevice(device); // 这里有HdfDeviceObject到MipiDsiCntlr的强制转化
// return (device == NULL) ? NULL : (struct MipiDsiCntlr *)device->service;
...
MipiTxDrvExit(); // 【必要】对厂商设备所占资源的释放
MipiTxDrvExit(); // 【必要】对设备所占资源的释放
MipiDsiUnregisterCntlr(&g_mipiTx); // 空函数
g_mipiTx.priv = NULL;
HDF_LOGI("%s: unload mipi_tx driver 1212!", __func__);
......
......@@ -30,22 +30,23 @@ Regulator接口定义了操作Regulator设备的通用方法集合,包括:
电源管理芯片,内含多个电源甚至其他子系统。
### 运作机制
在HDF框架中,Regulator模块接口适配模式采用统一服务模式,这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源
在HDF框架中,Regulator模块接口适配模式采用统一服务模式(如图1),这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问,实现便捷管理和节约资源的目的
Regulator模块各分层的作用为:接口层提供打开设备,写入数据,关闭设备接口的能力。核心层主要提供绑定设备、初始化设备以及释放设备的能力。适配层实现其他具体的功能。
Regulator模块各分层的作用为:
![](../public_sys-resources/icon-note.gif) 说明:<br>核心层可以调用接口层的函数,也可以通过钩子函数调用适配层函数,从而使得适配层间接的可以调用接口层函数,但是不可逆转接口层调用适配层函数。
- 接口层:提供打开设备,操作Regulator,关闭设备的能力。
- 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取Regulator设备的能力。
- 适配层:由驱动适配者实现与硬件相关的具体功能,如设备的初始化等。
**图 1** 统一服务模式结构图
**图 1** Regulator统一服务模式结构图<a name="fig1"></a>
![image1](figures/统一服务模式结构图.png)
### 约束与限制
Regulator模块当前仅支持轻量和小型系统内核(LiteOS)
Regulator模块API当前仅支持内核态调用
## 使用指导
......@@ -58,27 +59,25 @@ Regulator主要用于:
### 接口说明
Regulator模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/regulator_if.h。
**表1** Regulator设备API接口说明
| 接口名 | 描述 |
| 接口名 | 接口描述 |
| --------------------- | ------------------------- |
| RegulatorOpen | 获取Regulator设备驱动句柄 |
| RegulatorClose | 销毁Regulator设备驱动句柄 |
| RegulatorEnable | 使能Regulator |
| RegulatorDisable | 禁用Regulator |
| RegulatorForceDisable | 强制禁用Regulator |
| RegulatorSetVoltage | 设置Regulator输出电压 |
| RegulatorGetVoltage | 获取Regulator输出电压 |
| RegulatorSetCurrent | 设置Regulator输出电流 |
| RegulatorGetCurrent | 获取Regulator输出电流 |
| RegulatorGetStatus | 获取Regulator状态 |
| DevHandle RegulatorOpen(const char \*name) | 获取Regulator设备驱动句柄 |
| void RegulatorClose(DevHandle handle) | 销毁Regulator设备驱动句柄 |
| int32_t RegulatorEnable(DevHandle handle) | 使能Regulator |
| int32_t RegulatorDisable(DevHandle handle) | 禁用Regulator |
| int32_t RegulatorForceDisable(DevHandle handle) | 强制禁用Regulator |
| int32_t RegulatorSetVoltage(DevHandle handle, uint32_t minUv, uint32_t maxUv) | 设置Regulator输出电压 |
| int32_t RegulatorGetVoltage(DevHandle handle, uint32_t \*voltage) | 获取Regulator输出电压 |
| int32_t RegulatorSetCurrent(DevHandle handle, uint32_t minUa, uint32_t maxUa) | 设置Regulator输出电流 |
| int32_t RegulatorGetCurrent(DevHandle handle, uint32_t \*regCurrent) | 获取Regulator输出电流 |
| int32_t RegulatorGetStatus(DevHandle handle, uint32_t \*status) | 获取Regulator状态 |
### 开发步骤
在操作系统启动过程中,驱动管理模块根据配置文件加载Regulator驱动,Regulator驱动会检测Regulator器件并初始化驱动。
使用Regulator设备的一般流程如图2所示。
**图 2** Regulator设备使用流程图
......@@ -89,7 +88,7 @@ Regulator主要用于:
在操作Regulator设备时,首先要调用RegulatorOpen获取Regulator设备句柄,该函数会返回指定设备名称的Regulator设备句柄。
```
```c
DevHandle RegulatorOpen(const char *name);
```
......@@ -104,7 +103,7 @@ DevHandle RegulatorOpen(const char *name);
```
```c
/* Regulator设备名称 */
const char *name = "regulator_virtual_1";
DevHandle handle = NULL;
......@@ -120,7 +119,7 @@ if (handle == NULL) {
关闭Regulator设备,系统释放对应的资源。
```
```c
void RegulatorClose(DevHandle handle);
```
......@@ -130,7 +129,7 @@ void RegulatorClose(DevHandle handle);
| ------ | ----------------- |
| handle | Regulator设备句柄 |
```
```c
/* 销毁Regulator设备句柄 */
RegulatorClose(handle);
```
......@@ -139,7 +138,7 @@ RegulatorClose(handle);
启用Regulator设备。
```
```c
int32_t RegulatorEnable(DevHandle handle);
```
......@@ -154,7 +153,7 @@ int32_t RegulatorEnable(DevHandle handle);
```
```c
int32_t ret;
/* 启用Regulator设备 */
......@@ -168,7 +167,7 @@ if (ret != 0) {
禁用Regulator设备。如果Regulator设备状态为常开,或存在Regulator设备子节点未禁用,则禁用失败。
```
```c
int32_t RegulatorDisable(DevHandle handle);
```
......@@ -181,7 +180,7 @@ int32_t RegulatorDisable(DevHandle handle);
| 0 | 禁用成功 |
| 负数 | 禁用失败 |
```
```c
int32_t ret;
/* 禁用Regulator设备 */
......@@ -195,7 +194,7 @@ if (ret != 0) {
强制禁用Regulator设备。无论Regulator设备的状态是常开还是子节点已使能,Regulator设备都会被禁用。
```
```c
int32_t RegulatorForceDisable(DevHandle handle);
```
......@@ -209,7 +208,7 @@ int32_t RegulatorForceDisable(DevHandle handle);
| 0 | 禁用成功 |
| 负数 | 禁用失败 |
```
```c
int32_t ret;
/* 强制禁用Regulator设备 */
......@@ -221,9 +220,7 @@ if (ret != 0) {
#### 设置Regulator输出电压范围
设置Regulator电压输出电压范围。
```
```c
int32_t RegulatorSetVoltage(DevHandle handle, uint32_t minUv, uint32_t maxUv);
```
......@@ -238,7 +235,7 @@ int32_t RegulatorSetVoltage(DevHandle handle, uint32_t minUv, uint32_t maxUv);
| 0 | 设置成功 |
| 负数 | 设置失败 |
```
```c
int32_t ret;
int32_t minUv = 0; // 最小电压为0µV
int32_t maxUv = 20000; // 最大电压为20000µV
......@@ -252,9 +249,7 @@ if (ret != 0) {
#### 获取Regulator电压
获取Regulator电压。
```
```c
int32_t RegulatorGetVoltage(DevHandle handle, uint32_t *voltage);
```
......@@ -269,7 +264,7 @@ int32_t RegulatorGetVoltage(DevHandle handle, uint32_t *voltage);
| 0 | 获取成功 |
| 负数 | 获取失败 |
```
```c
int32_t ret;
uint32_t voltage;
......@@ -282,9 +277,7 @@ if (ret != 0) {
#### 设置Regulator输出电流范围
设置Regulator输出电流范围。
```
```c
int32_t RegulatorSetCurrent(DevHandle handle, uint32_t minUa, uint32_t maxUa);
```
......@@ -299,7 +292,7 @@ int32_t RegulatorSetCurrent(DevHandle handle, uint32_t minUa, uint32_t maxUa);
| 0<br>| 设置成功 |
| 负数 | 设置失败 |
```
```c
int32_t ret;
int32_t minUa = 0; // 最小电流为0μA
int32_t maxUa = 200; // 最大电流为200μA
......@@ -313,9 +306,7 @@ if (ret != 0) {
#### 获取Regulator电流
获取Regulator电流。
```
```c
int32_t RegulatorGetCurrent(DevHandle handle, uint32_t *regCurrent);
```
......@@ -329,7 +320,7 @@ int32_t RegulatorGetCurrent(DevHandle handle, uint32_t *regCurrent);
| 0 | 获取成功 |
| 负数 | 获取失败 |
```
```c
int32_t ret;
uint32_t regCurrent;
......@@ -342,9 +333,7 @@ if (ret != 0) {
#### 获取Regulator状态
获取Regulator状态。
```
```c
int32_t RegulatorGetStatus(DevHandle handle, uint32_t *status);
```
......@@ -358,7 +347,7 @@ int32_t RegulatorGetStatus(DevHandle handle, uint32_t *status);
| 0 | 获取成功 |
| 负数 | 获取失败 |
```
```c
int32_t ret;
uint32_t status;
......@@ -373,9 +362,11 @@ if (ret != 0) {
## 使用实例
本例拟对Hi3516DV300开发板上Regulator设备进行简单的读取操作。
Regulator设备完整的使用示例如下所示,首先获取Regulator设备句柄,然后使能,设置电压,获取电压、状态,禁用,最后销毁Regulator设备句柄。
```
```c
void RegulatorTestSample(void)
{
int32_t ret;
......
......@@ -5,42 +5,41 @@
### 功能简介
Regulator模块用于控制系统中某些设备的电压/电流供应。在嵌入式系统(尤其是手机)中,控制耗电量很重要,直接影响到电池的续航时间。所以,如果系统中某一个模块暂时不需要使用,就可以通过Regulator关闭其电源供应;或者降低提供给该模块的电压、电流大小。
Regulator模块用于控制系统中各类设备的电压/电流供应。在嵌入式系统(尤其是手机)中,控制耗电量很重要,直接影响到电池的续航时间。所以,如果系统中某一个模块暂时不需要使用,就可以通过Regulator关闭其电源供应;或者降低提供给该模块的电压、电流大小。
### 运作机制
在HDF框架中,Regulator模块接口适配模式采用统一服务模式,这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
在HDF框架中,Regulator模块接口适配模式采用统一服务模式(如图1),这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
Regulator模块各分层的作用为:
- 接口层提供打开设备,写入数据,关闭设备接口的能力。
- 核心层主要提供绑定设备、初始化设备以及释放设备的能力。
- 适配层实现其他具体的功能。
![](../public_sys-resources/icon-note.gif) 说明:<br>核心层可以调用接口层的函数,也可以通过钩子函数调用适配层函数,从而使得适配层间接的可以调用接口层函数,但是不可逆转接口层调用适配层函数。
- 接口层:提供打开设备,操作Regulator,关闭设备的能力。
- 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取Regulator设备的能力。
- 适配层:由驱动适配者实现与硬件相关的具体功能,如设备的初始化等。
**图 1** 统一服务模式结构图
![image1](figures/统一服务模式结构图.png)
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
**图 1** 统一服务模式结构图<a name="fig1"></a>
![image1](figures/统一服务模式结构图.png)
### 约束与限制
Regulator模块当前仅支持轻量和小型系统内核(LiteOS)
Regulator模块当前仅支持小型系统
## 开发指导
### 场景介绍
Regulator模块用于控制系统中某些设备的电压/电流供应。
Regulator模块用于控制系统中某些设备的电压/电流供应。当驱动开发者需要将Regulator设备适配到OpenHarmony时,需要进行Regulator驱动适配,下文将介绍如何进行Regulator驱动适配。
### 接口说明
通过以下RegulatorMethod中的函数调用Regulator驱动对应的函数
为了保证上层在调用Regulator接口时能够正确的操作硬件,核心层在//drivers/hdf_core/framework/support/platform/include/regulator/regulator_core.h中定义了以下钩子函数。驱动适配者需要在适配层实现这些函数的具体功能,并与这些钩子函数挂接,从而完成接口层与核心层的交互
RegulatorMethod定义:
```
```c
struct RegulatorMethod {
int32_t (*open)(struct RegulatorNode *node);
int32_t (*close)(struct RegulatorNode *node);
......@@ -56,8 +55,7 @@ struct RegulatorMethod {
};
```
**表 1** RegulatorMethod 结构体成员的回调函数功能说明
**表 1** RegulatorMethod 结构体成员的钩子函数功能说明
| 成员函数 | 入参 | 返回值 | 功能 |
| ------------ | ----------------------------------------------------------- | ----------------- | ---------------- |
......@@ -90,20 +88,20 @@ Regulator模块适配包含以下四个步骤:
一般在加载驱动时HDF会先调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
```
```c
struct HdfDriverEntry g_regulatorDriverEntry = {
.moduleVersion = 1,
.moduleName = "virtual_regulator_driver",// 【必要且与HCS文件中里面的moduleName匹配】
.moduleName = "virtual_regulator_driver", // 【必要且与HCS文件中里面的moduleName匹配】
.Init = VirtualRegulatorInit,
.Release = VirtualRegulatorRelease,
};
// 调用HDF_INIT将驱动入口注册到HDF框架中
/* 调用HDF_INIT将驱动入口注册到HDF框架中 */
HDF_INIT(g_regulatorDriverEntry);
```
2. 配置属性文件:
-vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
以Hi3516DV300开发板为例,在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。
deviceNode信息与驱动入口注册相关,器件属性值与核心层RegulatorNode成员的默认值或限制范围有密切关系。
......@@ -118,11 +116,11 @@ Regulator模块适配包含以下四个步骤:
| serviceName | 固定为HDF_PLATFORM_REGULATOR_MANAGER |
| deviceMatchAttr | 没有使用,可忽略 |
从第二个节点开始配置具体Regulator控制器信息,此节点并不表示某一路Regulator控制器,而是代表一个资源性质设备,用于描述一类Regulator控制器的信息。本例只有一个Regulator设备,如有多个设备,则需要在device_info文件增加deviceNode信息,以及在regulator\_config文件中增加对应的器件属性。
从第二个节点开始配置具体Regulator控制器信息,此节点并不表示某一路Regulator控制器,而是代表一个资源性质设备,用于描述一类Regulator控制器的信息。本例只有一个Regulator设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在regulator\_config文件中增加对应的器件属性。
- device_info.hcs 配置参考
```
```c
root {
device_info {
platform :: host {
......@@ -130,12 +128,12 @@ Regulator模块适配包含以下四个步骤:
priority = 50;
device_regulator :: device {
device0 :: deviceNode { // 为每一个Regulator控制器配置一个HDF设备节点,存在多个时添加,否则不用。
policy = 1; // 2:用户态可见;1:内核态可见;0:不需要发布服务。
policy = 1; // 2:用户态、内核态均可见;1:内核态可见;0:不需要发布服务。
priority = 50; // 驱动启动优先级
permission = 0644; // 驱动创建设备节点权限
/* 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 */
moduleName = "HDF_PLATFORM_REGULATOR_MANAGER";
serviceName = "HDF_PLATFORM_REGULATOR_MANAGER"; //【必要且唯一】驱动对外发布服务的名称
serviceName = "HDF_PLATFORM_REGULATOR_MANAGER"; // 【必要且唯一】驱动对外发布服务的名称
/* 【必要】用于配置控制器私有数据,要与regulator_config.hcs中对应控制器保持一致,具体的控制器信息在regulator_config.hcs中。 */
deviceMatchAttr = "hdf_platform_regulator_manager";
}
......@@ -154,7 +152,7 @@ Regulator模块适配包含以下四个步骤:
- regulator\_config.hcs配置参考
```
```c
root {
platform {
regulator_config {
......@@ -198,16 +196,24 @@ Regulator模块适配包含以下四个步骤:
}
```
需要注意的是,新增regulator_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
例如:本例中regulator_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/regulator/regulator_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
```c
#include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/regulator/regulator_config.hcs" // 配置文件相对路径
```
3. 实例化核心层接口函数:
- 完成驱动入口注册之后,下一步就是对核心层RegulatorNode对象的初始化,包括厂商自定义结构体(传递参数和数据),实例化RegulatorNode成员RegulatorMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
完成驱动入口注册之后,下一步就是对核心层RegulatorNode对象的初始化,包括驱动适配者自定义结构体(传递参数和数据),实例化RegulatorNode成员RegulatorMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
- 自定义结构体参考。
从驱动的角度看,RegulatorNode结构体是参数和数据的载体,HDF框架通过DeviceResourceIface将regulator\_config.hcs文件中的数值读入其中。
```
// RegulatorNode是核心层控制器结构体,其中的成员在Init函数中会被赋值。
```c
/* RegulatorNode是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */
struct RegulatorNode {
struct RegulatorDesc regulatorInfo;
struct DListHead node;
......@@ -217,35 +223,33 @@ Regulator模块适配包含以下四个步骤:
};
struct RegulatorDesc {
const char *name; /* regulator名称 */
const char *parentName; /* regulator父节点名称 */
struct RegulatorConstraints constraints; /* regulator约束信息 */
uint32_t minUv; /* 最小输出电压值 */
uint32_t maxUv; /* 最大输出电压值 */
uint32_t minUa; /* 最小输出电流值 */
uint32_t maxUa; /* 最大输出电流值 */
uint32_t status; /* regulator的状态,开或关。*/
const char *name; // regulator名称
const char *parentName; // regulator父节点名称
struct RegulatorConstraints constraints; // regulator约束信息
uint32_t minUv; // 最小输出电压值
uint32_t maxUv; // 最大输出电压值
uint32_t minUa; // 最小输出电流值
uint32_t maxUa; // 最大输出电流值
uint32_t status; // regulator的状态,开或关。
int useCount;
int consumerRegNums; /* regulator用户数量 */
RegulatorStatusChangecb cb; /* 当regulator状态改变时,可通过此变量通知。*/
int consumerRegNums; // regulator用户数量
RegulatorStatusChangecb cb; // 当regulator状态改变时,可通过此变量通知。
};
struct RegulatorConstraints {
uint8_t alwaysOn; /* regulator是否常开 */
uint8_t mode; /* 模式:电压或者电流 */
uint32_t minUv; /* 最小可设置输出电压 */
uint32_t maxUv; /* 最大可设置输出电压 */
uint32_t minUa; /* 最小可设置输出电流 */
uint32_t maxUa; /* 最大可设置输出电流 */
uint8_t alwaysOn; // regulator是否常开
uint8_t mode; // 模式:电压或者电流
uint32_t minUv; // 最小可设置输出电压
uint32_t maxUv; // 最大可设置输出电压
uint32_t minUa; // 最小可设置输出电流
uint32_t maxUa; // 最大可设置输出电流
};
```
- 实例化RegulatorNode成员RegulatorMethod,其他成员在Init函数中初始化。
```c
// regulator_virtual.c中的示例:钩子函数的填充
/* regulator_virtual.c中的示例:钩子函数的填充 */
static struct RegulatorMethod g_method = {
.enable = VirtualRegulatorEnable,
.disable = VirtualRegulatorDisable,
......@@ -257,17 +261,15 @@ Regulator模块适配包含以下四个步骤:
};
```
- Init函数参考
- Init函数开发参考
入参:
HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
返回值:
HDF\_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/framework/include/utils/hdf\_base.h中HDF\_STATUS定义)。
HDF\_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf\_core/framework/include/utils/hdf\_base.h中HDF\_STATUS定义)。
**表 2** HDF\_STATUS相关状态
......@@ -284,7 +286,6 @@ Regulator模块适配包含以下四个步骤:
初始化自定义结构体和RegulatorNode成员,并通过调用核心层RegulatorNodeAdd函数挂载Regulator控制器。
```c
static int32_t VirtualRegulatorInit(struct HdfDeviceObject *device)
{
......@@ -292,7 +293,7 @@ Regulator模块适配包含以下四个步骤:
const struct DeviceResourceNode *childNode = NULL;
...
DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) {
ret = VirtualRegulatorParseAndInit(device, childNode);// 【必要】实现见下
ret = VirtualRegulatorParseAndInit(device, childNode); // 【必要】实现见下
...
}
...
......@@ -304,7 +305,7 @@ Regulator模块适配包含以下四个步骤:
struct RegulatorNode *regNode = NULL;
(void)device;
regNode = (struct RegulatorNode *)OsalMemCalloc(sizeof(*regNode));//加载HCS文件
regNode = (struct RegulatorNode *)OsalMemCalloc(sizeof(*regNode)); //加载HCS文件
...
ret = VirtualRegulatorReadHcs(regNode, node); // 读取HCS文件信息
...
......@@ -316,11 +317,11 @@ Regulator模块适配包含以下四个步骤:
}
```
- Release 函数参考
- Release函数开发参考
入参:
HdfDeviceObject是整个驱动对外暴露的接口参数,其包含了HCS配置文件中的相关配置信息。
HdfDeviceObject是整个驱动对外提供的接口参数,其包含了HCS配置文件中的相关配置信息。
返回值:
......@@ -334,12 +335,10 @@ Regulator模块适配包含以下四个步骤:
static void VirtualRegulatorRelease(struct HdfDeviceObject *device)
{
...
RegulatorNodeRemoveAll();// 【必要】调用核心层函数,释放RegulatorNode的设备和服务
RegulatorNodeRemoveAll(); // 【必要】调用核心层函数,释放RegulatorNode的设备和服务
}
```
4. 驱动调试:
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。
# RTC
## 概述
RTC(Real-time Clock)为操作系统中的实时时钟设备。在HDF框架中,RTC的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
### 功能简介
**图1** RTC独立服务模式结构图
RTC(real-time clock)为操作系统中的实时时钟设备,为操作系统提供精准的实时时间和定时报警功能。当设备下电后,通过外置电池供电,RTC继续记录操作系统时间;设备上电后,RTC提供实时时钟给操作系统,确保断电后系统时间的连续性。
![image](figures/独立服务模式结构图.png "RTC独立服务模式结构图")
### 运作机制
在HDF框架中,RTC的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
## 接口说明
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
RtcMethod定义:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
**图1** RTC独立服务模式结构图<a name="fig1"></a>
```
![image](figures/独立服务模式结构图.png "RTC独立服务模式结构图")
## 开发指导
### 场景介绍
RTC主要用于提供实时时间和定时报警功能。当驱动开发者需要将RTC设备适配到OpenHarmony时,需要进行RTC驱动适配,下文将介绍如何进行RTC驱动适配。
### 接口说明
为了保证上层在调用RTC接口时能够正确的操作硬件,核心层在//drivers/hdf_core/framework/support/platform/include/rtc/rtc_core.h中定义了以下钩子函数。驱动适配者需要在适配层实现这些函数的具体功能,并与这些钩子函数挂接,从而完成接口层与核心层的交互。
RtcMethod定义:
```c
struct RtcMethod {
int32_t (*ReadTime)(struct RtcHost *host, struct RtcTime *time);
int32_t (*WriteTime)(struct RtcHost *host, const struct RtcTime *time);
......@@ -31,7 +47,7 @@ struct RtcMethod {
};
```
**表1** RtcMethod结构体成员的回调函数功能说明
**表1** RtcMethod结构体成员的钩子函数功能说明
| 函数 | 入参 | 出参 | 返回值 | 功能 |
| -------- | -------- | -------- | -------- | -------- |
......@@ -48,7 +64,7 @@ struct RtcMethod {
| WriteReg | host:结构体指针,核心层RTC控制器<br>usrDefIndex:结构体,用户自定义寄存器索引<br>value:uint8_t,寄存器传入值 | 无 | HDF_STATUS相关状态 | 按照用户定义的寄存器索引,设置对应的寄存器配置,一个索引对应一字节的配置值 |
## 开发步骤
### 开发步骤
RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置属性文件,以及填充核心层接口函数。
......@@ -71,9 +87,9 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
【可选】针对新增驱动程序,建议验证驱动基本功能,例如RTC控制状态,中断响应情况等。
## 开发实例
### 开发实例
下方将以rtc_hi35xx.c为示例,展示需要厂商提供哪些内容来完整实现设备功能。
下方将以Hi3516DV300的驱动//device/soc/hisilicon/common/platform/rtc/rtc_hi35xx.c为示例,展示驱动适配者需要提供哪些内容来完整实现设备功能。
1. 驱动开发首先需要实例化驱动入口。
......@@ -85,28 +101,28 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
RTC驱动入口参考:
```
```c
struct HdfDriverEntry g_rtcDriverEntry = {
.moduleVersion = 1,
.Bind = HiRtcBind, // 见Bind参考
.Init = HiRtcInit, // 见Init参考
.Release = HiRtcRelease, // 见Release参考
.Bind = HiRtcBind, // 见Bind开发参考
.Init = HiRtcInit, // 见Init开发参考
.Release = HiRtcRelease, // 见Release开发参考
.moduleName = "HDF_PLATFORM_RTC",// 【必要】且与HCS里面的名字匹配
};
//调用HDF_INIT将驱动入口注册到HDF框架中
/* 调用HDF_INIT将驱动入口注册到HDF框架中 */
HDF_INIT(g_rtcDriverEntry);
```
2. 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在rtc_config.hcs中配置器件属性。
2. 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在rtc_config.hcs中配置器件属性。
deviceNode信息与驱动入口注册相关,器件属性值与核心层RtcHost成员的默认值或限制范围有密切关系。
本例只有一个RTC控制器,如有多个器件信息,则需要在device_info文件增加deviceNode信息,以及在rtc_config文件中增加对应的器件属性。
本例只有一个RTC控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在rtc_config文件中增加对应的器件属性。
- device_info.hcs配置参考
```
```c
root {
device_info {
platform :: host {
......@@ -117,7 +133,7 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
permission = 0644; // 驱动创建设备节点权限
moduleName = "HDF_PLATFORM_RTC"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。
serviceName = "HDF_PLATFORM_RTC"; // 【必要】驱动对外发布服务的名称,必须唯一。
deviceMatchAttr = "hisilicon_hi35xx_rtc";// 【必要】需要与设备hcs文件中的match_attr匹配。
deviceMatchAttr = "hisilicon_hi35xx_rtc"; // 【必要】需要与设备hcs文件中的match_attr匹配。
}
}
}
......@@ -128,12 +144,12 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
- rtc_config.hcs配置参考
```
```c
root {
platform {
rtc_config {
controller_0x12080000 {
match_attr = "hisilicon_hi35xx_rtc";// 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
match_attr = "hisilicon_hi35xx_rtc"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致
rtcSpiBaseAddr = 0x12080000; // 地址映射相关
regAddrLength = 0x100; // 地址映射相关
irq = 37; // 中断号
......@@ -150,14 +166,21 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
}
```
3. 完成属性文件配置之后,下一步就是以核心层RtcHost对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化RtcHost成员RtcMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
需要注意的是,新增rtc_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。
例如:本例中rtc_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句:
```c
#include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs" // 配置文件相对路径
```
3. 完成属性文件配置之后,下一步就是以核心层RtcHost对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化RtcHost成员RtcMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。
- 自定义结构体参考。
从驱动的角度看,自定义结构体是参数和数据的载体,而且rtc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员。
```
```c
struct RtcConfigInfo {
uint32_t spiBaseAddr; // 地址映射相关
volatile void *remapBaseAddr; // 地址映射相关
......@@ -172,7 +195,7 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
struct OsalMutex mutex; // 互斥锁
};
// RtcHost是核心层控制器结构体,其中的成员在Init函数中会被赋值。
/* RtcHost是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */
struct RtcHost {
struct IDeviceIoService service;
struct HdfDeviceObject *device;
......@@ -180,11 +203,11 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
void *data;
};
```
- RtcHost成员回调函数结构体RtcMethod的实例化,其他成员在Init函数中初始化。
- RtcHost成员钩子函数结构体RtcMethod的实例化,其他成员在Init函数中初始化。
```
// rtc_hi35xx.c中的示例:钩子函数的填充
```c
/* rtc_hi35xx.c中的示例:钩子函数的填充 */
static struct RtcMethod g_method = {
.ReadTime = HiRtcReadTime,
.WriteTime = HiRtcWriteTime,
......@@ -200,15 +223,15 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
};
```
- Bind 函数参考
- Bind函数开发参考
入参:
HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
返回值:
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。
**表2** HDF_STATUS返回值描述
......@@ -225,25 +248,24 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
关联HdfDeviceObject对象和RtcHost。
```
```c
static int32_t HiRtcBind(struct HdfDeviceObject *device)
{
struct RtcHost *host = NULL;
host = RtcHostCreate(device); // 实际是申请内存并挂接device: host->device = device
// 使HdfDeviceObject与RtcHost可以相互转化的前提
...
device->service = &host->service;// 使HdfDeviceObject与RtcHost可以相互转化的前提
device->service = &host->service; // 使HdfDeviceObject与RtcHost可以相互转化的前提
// 方便后续通过调用RtcHostFromDevice实现全局性质的host
return HDF_SUCCESS;
}
```
- Init函数参考
- Init函数开发参考
入参:
HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
返回值:
......@@ -253,39 +275,39 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
初始化自定义结构体对象,初始化RtcHost成员。
```
```c
static int32_t HiRtcInit(struct HdfDeviceObject *device)
{
struct RtcHost *host = NULL;
struct RtcConfigInfo *rtcInfo = NULL;
...
host = RtcHostFromDevice(device);// 这里是HdfDeviceObject到RtcHost的强制转化
host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转换
rtcInfo = OsalMemCalloc(sizeof(*rtcInfo));
...
// HiRtcConfigData会从设备配置树中读取属性填充rtcInfo的supportAnaCtrl、supportLock、spiBaseAddr、regAddrLength、irq,
// 为HiRtcSwInit和HiRtcSwInit提供参数,当函数HiRtcSwInit和HiRtcSwInit内部执行失败后进行内存释放等操作。
/* HiRtcConfigData会从设备配置树中读取属性填充rtcInfo的supportAnaCtrl、supportLock、spiBaseAddr、regAddrLength、irq,
* 为HiRtcSwInit和HiRtcSwInit提供参数,当函数HiRtcSwInit和HiRtcSwInit内部执行失败后进行内存释放等操作。
*/
if (HiRtcConfigData(rtcInfo, device->property) != 0) {
...
}
if (HiRtcSwInit(rtcInfo) != 0) {// 地址映射以及中断注册相关
if (HiRtcSwInit(rtcInfo) != 0) { // 地址映射以及中断注册相关
...
}
if (HiRtcHwInit(rtcInfo) != 0) {// 初始化anaCtrl和lockAddr相关内容
if (HiRtcHwInit(rtcInfo) != 0) { // 初始化anaCtrl和lockAddr相关内容
...
}
host->method = &g_method;// RtcMethod的实例化对象的挂载
host->method = &g_method; // RtcMethod的实例化对象的挂载
host->data = rtcInfo; // 使RtcConfigInfo与RtcHost可以相互转化的前提
HDF_LOGI("Hdf dev service:%s init success!", HdfDeviceGetServiceName(device));
return HDF_SUCCESS;
}
```
- Release 函数参考
- Release函数开发参考
入参:
HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。
HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。
返回值:
......@@ -299,14 +321,14 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置
> 所有强制转换获取相应对象的操作前提是在Init或Bind函数中具备对应赋值的操作。
```
```c
static void HiRtcRelease(struct HdfDeviceObject *device)
{
struct RtcHost *host = NULL;
struct RtcConfigInfo *rtcInfo = NULL;
...
host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转化
rtcInfo = (struct RtcConfigInfo *)host->data;// 这里是RtcHost到RtcConfigInfo的强制转化
host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转换
rtcInfo = (struct RtcConfigInfo *)host->data; // 这里是RtcHost到RtcConfigInfo的强制转换
if (rtcInfo != NULL) {
HiRtcSwExit(rtcInfo);
OsalMemFree(rtcInfo); // 释放RtcConfigInfo
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册