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

!10926 docs: Document rectification

Merge pull request !10926 from 贾子扬/master
# GPIO
## 概述
### 功能简介
GPIO(General-purpose input/output)即通用型输入输出。通常,GPIO控制器通过分组的方式管理所有GPIO管脚,每组GPIO有一个或多个寄存器与之关联,通过读写寄存器完成对GPIO管脚的操作。
GPIO接口定义了操作GPIO管脚的标准方法集合,包括:
- 设置管脚方向:方向可以是输入或者输出(暂不支持高阻态)
- 设置管脚方向:方向可以是输入或者输出(暂不支持高阻态)。
- 读写管脚电平值:电平值可以是低电平或高电平。
- 设置管脚中断服务函数:设置一个管脚的中断响应函数,以及中断触发方式。
- 使能和禁止管脚中断:禁止或使能管脚中断。
### 基本概念
GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。可以通过软件来控制其输入和输出,即I/O控制。
- GPIO输入
输入是检测各个引脚上的电平状态,高电平或者低电平状态。常见的输入模式有:模拟输入、浮空输入、上拉输入、下拉输入。
- 读写管脚电平值:电平值可以是低电平或高电平
- GPIO输出
- 设置管脚中断服务函数:设置一个管脚的中断响应函数,以及中断触发方式
输出是当需要控制引脚电平的高低时需要用到输出功能。常见的输出模式有:开漏输出、推挽输出、复用开漏输出、复用推挽输出。
- 使能和禁止管脚中断:禁止或使能管脚中断
### 运作机制
在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。GPIO模块接口适配模式采用统一服务模式(如图1所示)。
## 接口说明
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
**表1** GPIO驱动API接口功能介绍
GPIO模块各分层作用:
| 功能分类 | 接口描述 |
| -------- | -------- |
| GPIO读写 | -&nbsp;GpioRead:读管脚电平值<br/>-&nbsp;GpioWrite:写管脚电平值 |
| GPIO配置 | -&nbsp;GpioSetDir:设置管脚方向<br/>-&nbsp;GpioGetDir:获取管脚方向 |
| GPIO中断设置 | -&nbsp;GpioSetIrq:设置管脚对应的中断服务函数<br/>-&nbsp;GpioUnsetIrq:取消管脚对应的中断服务函数<br/>-&nbsp;GpioEnableIrq:使能管脚中断<br/>-&nbsp;GpioDisableIrq:禁止管脚中断 |
- 接口层提供操作GPIO管脚的标准方法。
- 核心层主要提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
> 本文涉及的所有接口,仅限内核态使用,不支持在用户态使用。
**图 1** GPIO统一服务模式结构图
![GPIO统一服务模式结构图](figures/统一服务模式结构图.png)
## 使用指导
### 场景介绍
GPIO仅是一个软件层面的概念,主要工作是GPIO管脚资源管理。开发者可以使用提供的GPIO操作接口,实现对管脚控制。
### 接口说明
GPIO模块提供的主要接口如表1所示。
**表1** GPIO驱动API接口功能介绍
| 接口名 | 描述 |
| ------------------------------------------------------------ | ------------------------------ |
| GpioGetByName(const char *gpioName) | 获取GPIO管脚ID |
| int32_t GpioRead(uint16_t gpio, uint16_t *val) | 读GPIO管脚电平值 |
| int32_t GpioWrite(uint16_t gpio, uint16_t val) | 写GPIO管脚电平值 |
| int32_t GpioGetDir(uint16_t gpio, uint16_t *dir) | 获取GPIO管脚方向 |
| int32_t GpioSetDir(uint16_t gpio, uint16_t dir) | 设置GPIO管脚方向 |
| int32_t GpioUnsetIrq(uint16_t gpio, void *arg); | 取消GPIO管脚对应的中断服务函数 |
| int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg) | 设置GPIO管脚对应的中断服务函数 |
| int32_t GpioEnableIrq(uint16_t gpio) | 使能GPIO管脚中断 |
| int32_t GpioDisableIrq(uint16_t gpio) | 禁止GPIO管脚中断 |
### 使用流程
>![](../public_sys-resources/icon-note.gif) **说明:**<br>
>本文涉及GPIO的所有接口,支持内核态及用户态使用。
### 开发步骤
GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流程如下图所示。
**图1** GPIO使用流程图
**图1** GPIO使用流程图
![image](figures/GPIO使用流程图.png "GPIO使用流程图")
#### 确定GPIO管脚号
![image](figures/GPIO使用流程图.png "GPIO使用流程图")
两种方式获取管脚号:根据SOC芯片规则进行计算、通过管脚别名获取
- 根据SOC芯片规则进行计算
### 确定GPIO管脚号
不同SOC芯片由于其GPIO控制器型号、参数、以及控制器驱动的不同,GPIO管脚号的换算方式不一样。
不同SOC芯片由于其GPIO控制器型号、参数、以及控制器驱动的不同,GPIO管脚号的换算方式不一样。
- Hi3516DV300
- Hi3516DV300
控制器管理12组GPIO管脚,每组8个。
GPIO号 = GPIO组索引 (0~11) \* 每组GPIO管脚数(8) + 组内偏移
举例:GPIO10_3的GPIO号 = 10 \* 8 + 3 = 83
- Hi3518EV300
- Hi3518EV300
控制器管理10组GPIO管脚,每组10个。
GPIO号 = GPIO组索引 (0~9) \* 每组GPIO管脚数(10) + 组内偏移
举例:GPIO7_3的GPIO管脚号 = 7 \* 10 + 3 = 73
- 通过管脚别名获取
### 使用API操作GPIO管脚
调用接口GpioGetByName进行获取,入参是该管脚的别名,接口返回值是管脚的全局ID。
- 设置GPIO管脚方向
在进行GPIO管脚读写前,需要先通过如下函数设置GPIO管脚方向:
```c
GpioGetByName(const char *gpioName);
```
int32_t GpioSetDir(uint16_t gpio, uint16_t dir);
#### 设置GPIO管脚方向
**表2** GpioSetDir参数和返回值描述
在进行GPIO管脚读写前,需要先通过如下函数设置GPIO管脚方向:
| **参数**| **参数描述** |
| -------- | -------- |
| gpio | 待设置的GPIO管脚号 |
| dir | 待设置的方向值 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| 负数 | 设置失败 |
```c
int32_t GpioSetDir(uint16_t gpio, uint16_t dir);
```
- 读写GPIO管脚
**表2** GpioSetDir参数和返回值描述
如果要读取一个GPIO管脚电平,通过以下函数完成:
| **参数** | **参数描述** |
| ---------- | ------------------ |
| gpio | GPIO管脚号 |
| dir | 待设置的方向值 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| 负数 | 设置失败 |
int32_t GpioRead(uint16_t gpio, uint16_t \*val);
假设需要将GPIO管脚3的方向配置为输出,其使用示例如下:
**表3** GpioRead参数和返回值描述
```c
int32_t ret;
| **参数** | **参数描述** |
| -------- | -------- |
| gpio | 待读取的GPIO管脚号 |
| val | 接收读取电平值的指针 |
| **返回值** | **返回值描述** |
| 0 | 读取成功 |
| 负数 | 读取失败 |
ret = GpioSetDir(3, GPIO_DIR_OUT); // 将3号GPIO管脚配置为输出
if (ret != 0) {
HDF_LOGE("GpioSerDir: failed, ret %d\n", ret);
return ret;
}
```
如果要向GPIO管脚写入电平值,通过以下函数完成:
#### 获取GPIO管脚方向
int32_t GpioWrite(uint16_t gpio, uint16_t val);
可以通过如下函数获取GPIO管脚方向:
**表4** GpioWrite参数和返回值描述
```c
int32_t GpioGetDir(uint16_t gpio, uint16_t *dir);
```
| **参数** | **参数描述** |
| -------- | -------- |
| gpio | 待写入的GPIO管脚号 |
| val | 待写入的电平值 |
| **返回值** | **返回值描述** |
| 0 | 写入成功 |
| 负数 | 写入失败 |
**表2** GpioGetDir参数和返回值描述
示例代码:
| **参数** | **参数描述** |
| ---------- | ------------------ |
| gpio | GPIO管脚号 |
| dir | 待获取的方向值 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| 负数 | 设置失败 |
假设需要将GPIO管脚3的方向配置为输出,其使用示例如下:
```
int32_t ret;
uint16_t val;
/* 将3号GPIO管脚配置为输出 */
ret = GpioSetDir(3, GPIO_DIR_OUT);
if (ret != 0) {
HDF_LOGE("GpioSerDir: failed, ret %d\n", ret);
return;
}
/* 向3号GPIO管脚写入低电平GPIO_VAL_LOW */
ret = GpioWrite(3, GPIO_VAL_LOW);
if (ret != 0) {
HDF_LOGE("GpioWrite: failed, ret %d\n", ret);
return;
}
/* 将6号GPIO管脚配置为输入 */
ret = GpioSetDir(6, GPIO_DIR_IN);
if (ret != 0) {
HDF_LOGE("GpioSetDir: failed, ret %d\n", ret);
return;
}
/* 读取6号GPIO管脚的电平值 */
ret = GpioRead(6, &val);
```
```c
int32_t ret;
uin16_t dir;
- 设置GPIO中断
ret = GpioGetDir(3, &dir); // 获取3号GPIO管脚方向
if (ret != 0) {
HDF_LOGE("GpioGetDir: failed, ret %d\n", ret);
return ret;
}
```
如果要为一个GPIO管脚设置中断响应程序,使用如下函数:
#### 读取GPIO管脚电平值
int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void \*arg);
如果要读取一个GPIO管脚电平,通过以下函数完成:
**表5** GpioSetIrq参数和返回值描述
```c
int32_t GpioRead(uint16_t gpio, uint16_t *val);
```
| **参数** | **参数描述** |
| -------- | -------- |
| gpio | GPIO管脚号 |
| mode | 中断触发模式 |
| func | 中断服务程序 |
| arg | 传递给中断服务程序的入参 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| 负数 | 设置失败 |
**表3** GpioRead参数和返回值描述
> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**<br>
> 同一时间,只能为某个GPIO管脚设置一个中断服务函数,如果重复调用GpioSetIrq函数,则之前设置的中断服务函数会被取代。
| **参数** | **参数描述** |
| ---------- | -------------------- |
| gpio | GPIO管脚号 |
| val | 接收读取电平值的指针 |
| **返回值** | **返回值描述** |
| 0 | 读取成功 |
| 负数 | 读取失败 |
当不再需要响应中断服务函数时,使用如下函数取消中断设置
假设需要读取GPIO管脚3的电平值,其使用示例如下
int32_t GpioUnsetIrq(uint16_t gpio, void \*arg);
```c
int32_t ret;
uint16_t val;
**表6** GpioUnsetIrq参数和返回值描述
ret = GpioRead(3, &val); // 读取3号GPIO管脚电平值
if (ret != 0) {
HDF_LOGE("GpioRead: failed, ret %d\n", ret);
return ret;
}
```
| **参数** | **参数描述** |
| -------- | -------- |
| gpio | GPIO管脚号 |
| arg | GPIO中断数据 |
| **返回值** | **返回值描述** |
| 0 | 取消成功 |
| 负数 | 取消失败 |
#### 写入GPIO管脚电平值
在中断服务程序设置完成后,还需要先通过如下函数使能GPIO管脚的中断
如果要向GPIO管脚写入电平值,通过以下函数完成
int32_t GpioEnableIrq(uint16_t gpio);
```c
int32_t GpioWrite(uint16_t gpio, uint16_t val);
```
**表7** GpioEnableIrq参数和返回值描述
**表4** GpioWrite参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| gpio | GPIO管脚号 |
| **返回值** | **返回值描述** |
| 0 | 使能成功 |
| 负数 | 使能失败 |
| **参数** | **参数描述** |
| ---------- | ------------------ |
| gpio | GPIO管脚号 |
| val | 待写入的电平值 |
| **返回值** | **返回值描述** |
| 0 | 写入成功 |
| 负数 | 写入失败 |
> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**<br>
> 必须通过此函数使能管脚中断,之前设置的中断服务函数才能被正确响应。
假设需要给GPIO管脚3写入低电平值,其使用示例如下:
如果要临时屏蔽此中断,可以通过如下函数禁止GPIO管脚中断:
```c
int32_t ret;
int32_t GpioDisableIrq(uint16_t gpio);
ret = GpioWrite(3, GPIO_VAL_LOW); // 给3号GPIO管脚写入低电平值
if (ret != 0) {
HDF_LOGE("GpioRead: failed, ret %d\n", ret);
return ret;
}
```
**表8** GpioDisableIrq参数和返回值描述
#### 设置GPIO管脚中断
| **参数** | **参数描述** |
| -------- | -------- |
| gpio | GPIO管脚号 |
| **返回值** | **返回值描述** |
| 0 | 禁止成功 |
| 负数 | 禁止失败 |
如果要为一个GPIO管脚设置中断响应程序,使用如下函数:
示例代码:
```c
int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg);
```
**表5** GpioSetIrq参数和返回值描述
```
/* 中断服务函数*/
int32_t MyCallBackFunc(uint16_t gpio, void *data)
{
HDF_LOGI("%s: gpio:%u interrupt service in! data=%p\n", __func__, gpio, data);
| **参数** | **参数描述** |
| ---------- | ------------------------ |
| gpio | GPIO管脚号 |
| mode | 中断触发模式 |
| func | 中断服务程序 |
| arg | 传递给中断服务程序的入参 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| 负数 | 设置失败 |
> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**<br>
> 同一时间,只能为某个GPIO管脚设置一个中断服务函数,如果重复调用GpioSetIrq函数,则之前设置的中断服务函数会被取代。
#### 取消GPIO管脚中断
当不再需要响应中断服务函数时,使用如下函数取消中断设置:
```c
int32_t GpioUnsetIrq(uint16_t gpio, void *arg);
```
**表6** GpioUnsetIrq参数和返回值描述
| **参数** | **参数描述** |
| ---------- | -------------- |
| gpio | GPIO管脚号 |
| arg | GPIO中断数据 |
| **返回值** | **返回值描述** |
| 0 | 取消成功 |
| 负数 | 取消失败 |
#### 使能GPIO管脚中断
在中断服务程序设置完成后,还需要先通过如下函数使能GPIO管脚的中断:
```c
int32_t GpioEnableIrq(uint16_t gpio);
```
**表7** GpioEnableIrq参数和返回值描述
| **参数** | **参数描述** |
| ---------- | -------------- |
| gpio | GPIO管脚号 |
| **返回值** | **返回值描述** |
| 0 | 使能成功 |
| 负数 | 使能失败 |
> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**<br>
> 必须通过此函数使能管脚中断,之前设置的中断服务函数才能被正确响应。
#### 禁止GPIO管脚中断
如果要临时屏蔽此中断,可以通过如下函数禁止GPIO管脚中断:
```c
int32_t GpioDisableIrq(uint16_t gpio);
```
**表8** GpioDisableIrq参数和返回值描述
| **参数** | **参数描述** |
| ---------- | -------------- |
| gpio | GPIO管脚号 |
| **返回值** | **返回值描述** |
| 0 | 禁止成功 |
| 负数 | 禁止失败 |
中断相关操作示例:
```c
/* 中断服务函数*/
int32_t MyCallBackFunc(uint16_t gpio, void *data)
{
HDF_LOGI("%s: gpio:%u interrupt service in data\n", __func__, gpio);
return 0;
}
}
int32_t ret;
/* 设置中断服务程序为MyCallBackFunc,入参为NULL,中断触发模式为上升沿触发 */
ret = GpioSetIrq(3, OSAL_IRQF_TRIGGER_RISING, MyCallBackFunc, NULL);
if (ret != 0) {
int32_t ret;
/* 设置中断服务程序为MyCallBackFunc,入参为NULL,中断触发模式为上升沿触发 */
ret = GpioSetIrq(3, OSAL_IRQF_TRIGGER_RISING, MyCallBackFunc, NULL);
if (ret != 0) {
HDF_LOGE("GpioSetIrq: failed, ret %d\n", ret);
return;
}
return ret;
}
/* 使能3号GPIO管脚中断 */
ret = GpioEnableIrq(3);
if (ret != 0) {
/* 使能3号GPIO管脚中断 */
ret = GpioEnableIrq(3);
if (ret != 0) {
HDF_LOGE("GpioEnableIrq: failed, ret %d\n", ret);
return;
}
return ret;
}
/* 禁止3号GPIO管脚中断 */
ret = GpioDisableIrq(3);
if (ret != 0) {
/* 禁止3号GPIO管脚中断 */
ret = GpioDisableIrq(3);
if (ret != 0) {
HDF_LOGE("GpioDisableIrq: failed, ret %d\n", ret);
return;
}
return ret;
}
/* 取消3号GPIO管脚中断服务程序 */
ret = GpioUnsetIrq(3, NULL);
if (ret != 0) {
/* 取消3号GPIO管脚中断服务程序 */
ret = GpioUnsetIrq(3, NULL);
if (ret != 0) {
HDF_LOGE("GpioUnSetIrq: failed, ret %d\n", ret);
return;
}
```
return ret;
}
```
## 使用实例
本实例程序中,我们将测试一个GPIO管脚的中断触发:为管脚设置中断服务函数,触发方式为边沿触发,然后通过交替写高低电平到管脚,产生电平波动,制造触发条件,观察中断服务函数的执行。
首先需要选取一个空闲的GPIO管脚,本例程基于Hi3516DV300开发板,GPIO管脚选择GPIO10_3,换算成GPIO号为83。
首先需要选取一个空闲的GPIO管脚,本例程基于Hi3516DV300开发板,GPIO管脚选择GPIO10_3,换算成GPIO号为83。
读者可以根据自己使用的开发板,参考其原理图,选择一个空闲的GPIO管脚即可。
读者可以根据自己使用的开发板,参考其原理图,选择一个空闲的GPIO管脚即可。
```
```c
#include "gpio_if.h"
#include "hdf_log.h"
#include "osal_irq.h"
......@@ -261,7 +366,7 @@ static uint32_t g_irqCnt;
/* 中断服务函数*/
static int32_t TestCaseGpioIrqHandler(uint16_t gpio, void *data)
{
HDF_LOGE("%s: irq triggered! on gpio:%u, data=%p", __func__, gpio, data);
HDF_LOGE("%s: irq triggered! on gpio:%u, in data", __func__, gpio);
g_irqCnt++; /* 如果中断服务函数触发执行,则将全局中断计数加1 */
return GpioDisableIrq(gpio);
}
......
......@@ -4,15 +4,18 @@
### 功能简介<a name="section2"></a>
PIN即管脚控制器,用于统一管理各SoC厂商管脚资源,对外提供管脚复用功能:包括管脚推拉方式、管脚推拉强度以及管脚功能。
PIN即管脚控制器,用于统一管理各SoC的管脚资源,对外提供管脚复用功能:包括管脚推拉方式、管脚推拉强度以及管脚功能。
PIN接口定义了操作PIN管脚的通用方法集合,包括:
- 获取/释放管脚描述句柄: 传入管脚名与链表中每个控制器下管脚名进行匹配,匹配则会获取一个管脚描述句柄,操作完PIN管脚后释放该管脚描述句柄。
- 获取/释放管脚描述句柄:传入管脚名与链表中每个控制器下管脚名进行匹配,匹配则会获取一个管脚描述句柄,操作完PIN管脚后释放该管脚描述句柄。
- 设置/获取管脚推拉方式:推拉方式可以是上拉、下拉以及悬空。
- 设置/获取管脚推拉强度:用户可根据实际设置管脚推拉强度大小。
- 设置/获取管脚功能:通过管脚功能名设置/获取管脚功能,实现管脚复用。
### 基本概念<a name="section3"></a>
PIN是一个软件层面的概念,目的是为了统一各SoC厂商PIN管脚管理,对外提供管脚复用功能,配置PIN管脚的电气特性。
PIN是一个软件层面的概念,目的是为了统一各SoC的PIN管脚管理,对外提供管脚复用功能,配置PIN管脚的电气特性。
- SoC(System on Chip)
......@@ -24,50 +27,53 @@ PIN是一个软件层面的概念,目的是为了统一各SoC厂商PIN管脚
### 运作机制<a name="section4"></a>
在HDF框架中,PIN模块暂不支持用户态,所以不需要发布服务。接口适配模式采用无服务模式,用于不需要在用户态提供API的设备类型。对于没有用户态和内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址(DevHandle是一个void类型指针)。
在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。PIN模块接口适配模式采用统一服务模式。
在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。
PIN模块各分层作用:
- 接口层提供获取PIN管脚、设置PIN管脚推拉方式、获取PIN管脚推拉方式、设置PIN管脚推拉强度、获取PIN管脚推拉强度、设置PIN管脚功能、获取PIN管脚功能、释放PIN管脚的接口。
- 核心层主要提供PIN管脚资源匹配,PIN管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
**图 1** PIN无服务模式<a name="fig14423182615525"></a>
![](figures/无服务模式结构图.png "PIN无服务模式")
**图 1** PIN统一服务模式
![PIN统一服务模式](figures/统一服务模式结构图.png "统一服务模式")
### 约束与限制<a name="section5"></a>
PIN模块目前仅支持轻量和小型系统内核(LiteOS)
PIN模块目前只支持小型系统LiteOS-A内核
## 使用指导<a name="section6"></a>
## 使用指导<a name="section6"></a>
### 场景介绍<a name="section7"></a>
### 场景介绍<a name="section7"></a>
PIN模块仅是一个软件层面的概念,主要工作是管脚资源管理。使用复用管脚时,通过设置管脚功能、设置管脚推拉方式、设置管脚推拉强度来适配指定场景的需求。
PIN模块仅是一个软件层面的概念,主要工作是管脚资源管理。使用复用管脚时,通过设置管脚功能、设置管脚推拉方式、设置管脚推拉强度来适配指定场景的需求。
### 接口说明<a name="section8"></a>
PIN模块提供的主要接口如[表1](#table1)所示,更多关于接口的介绍请参考对应的API接口文档。
PIN模块提供的主要接口如表1所示,更多关于接口的介绍请参考对应的API接口文档。
**表 1** PIN驱动API接口功能介绍
<a name="table1"></a>
| **接口名** | **描述** |
| ------------------------------------------------------------ | ---------------- |
| DevHandle PinGet(const char *pinName); | 获取管脚描述句柄 |
| void PinPut(DevHandle handle); | 释放管脚描述句柄 |
| int32_t PinSetPull(DevHandle handle, enum PinPullType pullType); | 设置管脚推拉方式 |
| int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType); | 获取管脚推拉方式 |
| int32_t PinSetStrength(DevHandle handle, uint32_t strength); | 设置管脚推拉强度 |
| int32_t PinGetStrength(DevHandle handle, uint32_t *strength); | 获取管脚推拉强度 |
| int32_t PinSetFunc(DevHandle handle, const char *funcName); | 设置管脚功能 |
| int32_t PinGetFunc(DevHandle handle, const char **funcName); | 获取管脚功能 |
| DevHandle PinGet(const char *pinName) | 获取管脚描述句柄 |
| void PinPut(DevHandle handle) | 释放管脚描述句柄 |
| int32_t PinSetPull(DevHandle handle, enum PinPullType pullType) | 设置管脚推拉方式 |
| int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType) | 获取管脚推拉方式 |
| int32_t PinSetStrength(DevHandle handle, uint32_t strength) | 设置管脚推拉强度 |
| int32_t PinGetStrength(DevHandle handle, uint32_t *strength) | 获取管脚推拉强度 |
| int32_t PinSetFunc(DevHandle handle, const char *funcName) | 设置管脚功能 |
| int32_t PinGetFunc(DevHandle handle, const char **funcName) | 获取管脚功能 |
>![](../public_sys-resources/icon-note.gif) **说明:**<br>
>本文涉及的所有接口,仅限内核态使用,不支持在用户态使用。
>本文涉及PIN的所有接口,支持内核态及用户态使用。
### 开发步骤<a name="section9"></a>
使用PIN设备的一般流程如[图2](#fig2)所示。
使用PIN设备的一般流程如图2所示。
**图 2** PIN使用流程图<a name="fig2"></a>
![](figures/PIN使用流程图.png "PIN使用流程图")
......@@ -76,7 +82,7 @@ PIN模块提供的主要接口如[表1](#table1)所示,更多关于接口的
在使用PIN进行管脚操作时,首先要调用PinGet获取管脚描述句柄,该函数会返回匹配传入管脚名的管脚描述句柄。
```
```c
DevHandle PinGet(const char *pinName);
```
......@@ -93,9 +99,10 @@ DevHandle PinGet(const char *pinName);
假设PIN需要操作的管脚名为P18,获取其管脚描述句柄的示例如下:
```
DevHandle handle = NULL; /* PIN管脚描述句柄 */
char pinName = "P18"; /* PIN管脚号 */
```c
DevHandle handle = NULL; // PIN管脚描述句柄
char pinName = "P18"; // PIN管脚名
handle = PinGet(pinName);
if (handle == NULL) {
HDF_LOGE("PinGet: get handle failed!\n");
......@@ -107,7 +114,7 @@ if (handle == NULL) {
PIN设置管脚推拉方式的函数如下所示:
```
```c
int32_t PinSetPull(DevHandle handle, enum PinPullType pullType);
```
......@@ -125,9 +132,10 @@ int32_t PinSetPull(DevHandle handle, enum PinPullType pullType);
假设PIN要设置的管脚推拉方式为上拉,其实例如下:
```
```c
int32_t ret;
enum PinPullType pullTypeNum;
/* PIN设置管脚推拉方式 */
pullTypeNum = 1;
ret = PinSetPull(handle, pullTypeNum);
......@@ -141,7 +149,7 @@ if (ret != HDF_SUCCESS) {
PIN获取管脚推拉方式的函数如下所示:
```
```c
int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType);
```
......@@ -159,9 +167,10 @@ int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType);
PIN获取管脚推拉方式的实例如下:
```
```c
int32_t ret;
enum PinPullType pullTypeNum;
/* PIN获取管脚推拉方式 */
ret = PinGetPull(handle, &pullTypeNum);
if (ret != HDF_SUCCESS) {
......@@ -174,7 +183,7 @@ if (ret != HDF_SUCCESS) {
PIN设置管脚推拉强度函数如下所示:
```
```c
int32_t PinSetStrength(DevHandle handle, uint32_t strength);
```
......@@ -192,7 +201,7 @@ int32_t PinSetStrength(DevHandle handle, uint32_t strength);
假设PIN要设置的管脚推拉强度为2,其实例如下:
```
```c
int32_t ret;
uint32_t strengthNum;
/* PIN设置管脚推拉强度 */
......@@ -208,7 +217,7 @@ if (ret != HDF_SUCCESS) {
PIN设置管脚推拉强度后,可以通过PIN获取管脚推拉强度接口来查看PIN管脚推拉强度,PIN获取管脚推拉强度的函数如下所示:
```
```c
int32_t PinGetStrength(DevHandle handle, uint32_t *strength);
```
......@@ -226,9 +235,10 @@ int32_t PinGetStrength(DevHandle handle, uint32_t *strength);
PIN获取管脚推拉强度的实例如下:
```
```c
int32_t ret;
uint32_t strengthNum;
/* PIN获取管脚推拉强度 */
ret = PinGetStrength(handle, &strengthNum);
if (ret != HDF_SUCCESS) {
......@@ -239,11 +249,11 @@ if (ret != HDF_SUCCESS) {
#### PIN设置管脚功能
管脚功能特指的是管脚复用的功能,每个管脚功能都不相同,管脚功能名详细可以参考[PIN配置hcs文件](https://gitee.com/openharmony/device_soc_hisilicon/blob/master/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs)
管脚功能特指的是管脚复用的功能,每个管脚功能都不相同,管脚功能名详细可以参考//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs
PIN设置管脚功能函数如下所示:
```
```c
int32_t PinSetFunc(DevHandle handle, const char *funcName);
```
......@@ -261,9 +271,10 @@ int32_t PinSetFunc(DevHandle handle, const char *funcName);
假设PIN需要设置的管脚功能为LSADC_CH1(ADC通道1),其实例如下:
```
```c
int32_t ret;
char funcName = "LSADC_CH1";
/* PIN设置管脚功能 */
ret = PinSetFunc(handle, funcName);
if (ret != HDF_SUCCESS) {
......@@ -276,7 +287,7 @@ if (ret != HDF_SUCCESS) {
PIN设置管脚功能后,可以通过PIN获取管脚功能接口来查看PIN管脚功能,PIN获取管脚功能的函数如下所示:
```
```c
int32_t PinGetFunc(DevHandle handle, const char **funcName);
```
......@@ -294,11 +305,12 @@ int32_t PinGetFunc(DevHandle handle, const char **funcName);
PIN获取管脚功能的实例如下:
```
```c
int32_t ret;
char *funcName;
char *funcName = NULL;
/* PIN获取管脚功能 */
ret = PinGetFunc(handle,&funcName);
ret = PinGetFunc(handle, &funcName);
if (ret != HDF_SUCCESS) {
HDF_LOGE("PinGetFunc: failed, ret %d\n", ret);
return ret;
......@@ -309,7 +321,7 @@ if (ret != HDF_SUCCESS) {
PIN不再进行任何操作后,需要释放PIN管脚描述管脚句柄,函数如下所示:
```
```c
void PinPut(DevHandle handle);
```
......@@ -325,13 +337,14 @@ void PinPut(DevHandle handle);
PIN销毁管脚描述句柄实例如下:
```
```c
PinPut(handle);
```
## 使用实例<a name="section10"></a>
使用PIN设置管脚相关属性完整使用可以参考如下示例代码,步骤主要如下:
下面将基于Hi3516DV300开发板展示使用PIN设置管脚相关属性完整操作,步骤主要如下:
1. 传入要设置的管脚名,获取PIN管脚描述句柄。
2. 通过PIN管脚描述句柄以及推拉方式pullTypeNum设置管脚推拉方式,如果操作失败则释放PIN管脚描述句柄。
3. 通过PIN管脚描述句柄,并用pullTypeNum承接获取的管脚推拉方式,如果操作失败则释放PIN管脚描述句柄。
......@@ -341,7 +354,7 @@ PinPut(handle);
6. 通过PIN管脚描述句柄,并用funName承接获取的管脚功能名,如果操作失败则释放PIN管脚描述句柄。
7. 使用完PIN后,不再对管脚进行操作,释放PIN管脚描述句柄。
```
```c
#include "hdf_log.h" /* 标准日志打印头文件 */
#include "pin_if.h" /* PIN标准接口头文件 */
......@@ -359,7 +372,7 @@ int32_t PinTestSample(void)
/* PIN获取管脚描述句柄 */
handle = PinGet(pinName);
if (handle == NULL) {
HDF_LOGE("PinGet: failed!\n");
HDF_LOGE("PinGet: pin get failed!\n");
return;
}
/* PIN设置管脚推拉方式为上拉 */
......@@ -406,3 +419,4 @@ ERR:
PinPut(handle);
return ret;
}
```
# PWM
## 概述
PWM是脉冲宽度调制(Pulse Width Modulation)的缩写,是一种对模拟信号电平进行数字编码并将其转换为脉冲的技术。常用于马达控制、背光亮度调节等。
### 功能简介
PWM即脉冲宽度调制(Pulse Width Modulation)的缩写,是一种对模拟信号电平进行数字编码并将其转换为脉冲的技术。
PWM接口定义了操作PWM设备的通用方法集合,包括:
- PWM设备句柄获取和释放。
- PWM周期、占空比、极性的设置。
- PWM设备句柄获取和释放
- PWM周期、占空比、极性的设置
- PWM使能和关闭
- PWM配置信息的获取和设置
### 基本概念
脉冲是“电脉冲”的简称,指电路中电流或电压短暂起伏的现象,其特点是突变和不连续性。脉冲的种类很多,常见的脉冲波形有:三角脉冲、尖脉冲、矩形脉冲、方形脉冲、梯形脉冲及阶梯脉冲等。脉冲的主要参数包括重复周期T(T=1/F,F为煎复频率)、脉冲幅度U、脉冲前沿上升时间ts、后沿下降时间t、脉冲宽度tk等。
### 运作机制
在HDF框架中,PWM接口适配模式采用独立服务模式(如图1所示)。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDF设备管理器的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
PWM模块各分层作用:
- 接口层提供打开PWM设备、设置PWM设备周期、设置PWM设备占空时间、设置PWM设备极性、设置PWM设备参数、获取PWM设备参数、使能PWM设备、禁止PWM设备、关闭PWM设备的接口。
- 核心层主要提供PWM控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
- PWM使能和关闭。
**图1** PWM独立服务模式结构图
- PWM配置信息的获取和设置。
![image1](figures/独立服务模式结构图.png "PWM独立服务模式结构图")
## 使用指导
### 场景介绍
通常情况下,在使用马达控制、背光亮度调节时会用到PWM模块。
### PwmConfig结构体
### 接口说明
**表1** PwmConfig结构体介绍
PWM模块设备属性如表1所示,PWM模块提供的主要接口如表2所示。
**表1** PwmConfig结构体介绍
| 名称 | 描述 |
| -------- | -------- |
......@@ -27,391 +56,353 @@ PWM接口定义了操作PWM设备的通用方法集合,包括:
| polarity | 极性:正极性/反极性。 |
| status | 状态:启用状态/禁用状态。 |
**表2** PWM驱动API接口功能介绍
## 接口说明
**表2** PWM驱动API接口功能介绍
| 功能分类 | 接口描述 |
| -------- | -------- |
| PWM句柄操作 | -&nbsp;PwmOpen:获取PWM设备驱动句柄<br/>-&nbsp;PwmClose:释放PWM设备驱动句柄 |
| 使能/禁用PWM | -&nbsp;PwmEnable:使能PWM<br/>-&nbsp;PwmDisable:禁用PWM |
| PWM配置操作 | -&nbsp;PwmSetPeriod:设置PWM周期<br/>-&nbsp;PwmSetDuty:设置PWM占空时间<br/>-&nbsp;PwmSetPolarity:设置PWM极性 |
| 设置/获取PWM配置信息 | -&nbsp;PwmSetConfig:设置PWM设备参数<br/>-&nbsp;PwmGetConfig:获取PWM设备参数 |
| 接口名 | |
| ------------------------------------------------------------ | ------------------- |
| DevHandle PwmOpen(uint32_t num) | 打开PWM设备 |
| void PwmClose(DevHandle handle) | 关闭PWM设备 |
| int32_t PwmSetPeriod(DevHandle handle, uint32_t period) | 设置PWM设备周期 |
| int32_t PwmSetDuty(DevHandle handle, uint32_t duty) | 设置PWM设备占空时间 |
| int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity) | 设置PWM设备极性 |
| int32_t PwmEnable(DevHandle handle) | 使能PWM设备 |
| int32_t PwmDisable(DevHandle handle) | 禁用PWM设备 |
| int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config) | 设置PWM设备参数 |
| int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config) | 获取PWM设备参数 |
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
> 本文涉及的所有接口,仅限内核态使用,不支持在用户态使用。
## 使用指导
> 本文涉及PWM的所有接口,支持内核态及用户态使用。
### 使用流程
### 开发步骤
使用PWM的一般流程如下图所示。
**图1** PWM使用流程图
![image](figures/PWM设备使用流程图.png "PWM设备使用流程图")
**图2** PWM使用流程图
![image2](figures/PWM设备使用流程图.png "PWM设备使用流程图")
### 获取PWM设备句柄
#### 获取PWM设备句柄
在操作PWM设备时,首先要调用PwmOpen获取PWM设备句柄,该函数会返回指定设备号的PWM设备句柄。
```
```c
DevHandle PwmOpen(uint32_t num);
```
**表3** PwmOpen参数和返回值描述
**表3** PwmOpen参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| num | PWM设备号 |
| num | PWM设备号 |
| **返回值** | **返回值描述** |
| handle | 获取成功,返回PWM设备句柄 |
| NULL | 获取失败 |
| handle | 打开PWM设备成功,返回PWM设备句柄 |
| NULL | 打开PWM设备失败 |
假设系统中的PWM设备号为0,获取该PWM设备句柄的示例如下:
```
uint32_t num = 0; /* PWM设备号 */
```c
uint32_t num = 0; // PWM设备号
DevHandle handle = NULL;
/* 获取PWM设备句柄 */
handle = PwmOpen(num);
handle = PwmOpen(num); // 打开PWM 0设备并获取PWM设备句柄
if (handle == NULL) {
/* 错误处理 */
HDF_LOGE("PwmOpen: open pwm_%u failed.\n", num);
return;
}
```
### 销毁PWM设备句柄
#### 销毁PWM设备句柄
关闭PWM设备,系统释放对应的资源。
```
voidPwmClose(DevHandle handle);
```c
void PwmClose(DevHandle handle);
```
**表4** PwmClose参数描述
**表4** PwmClose参数描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | PWM设备句柄 |
```
/* 销毁PWM设备句柄 */
PwmClose(handle);
```c
PwmClose(handle); // 关闭PWM设备销毁PWM设备句柄
```
#### 使能PWM设备
### 使能
启用PWM设备。
```
```c
int32_t PwmEnable(DevHandle handle);
```
**表5** PwmEnable参数和返回值描述
**表5** PwmEnable参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | PWM设备句柄 |
| **返回值** | **返回值描述** |
| 0 | 使能成功 |
| HDF_SUCCESS | 使能成功 |
| 负数 | 使能失败 |
```
```c
int32_t ret;
/*启用PWM设备*/
ret = PwmEnable(handle);
if (ret != 0) {
/*错误处理*/
ret = PwmEnable(handle); // 启用PWM设备
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmEnable: enable pwm failed, ret:%d\n", ret);
return ret;
}
```
#### 禁用PWM设备
### 禁用
禁用PWM设备。
```
```c
int32_t PwmDisable(DevHandle handle);
```
**表6** PwmDisable参数和返回值描述
**表6** PwmDisable参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | PWM设备句柄 |
| **返回值** | **返回值描述** |
| 0 | 禁用成功 |
| HDF_SUCCESS | 禁用成功 |
| 负数 | 禁用失败 |
```
```c
int32_t ret;
/* 禁用PWM设备 */
ret = PwmDisable(handle);
if (ret != 0) {
/* 错误处理 */
ret = PwmDisable(handle); // 禁用PWM设备
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmDisable: disable pwm failed, ret:%d\n", ret);
return ret;
}
```
#### 设置PWM设备周期
### 设置PWM设备周期
设置PWM设备周期。
```
```c
int32_t PwmSetPeriod(DevHandle handle, uint32_t period);
```
**表7** PwmSetPeriod参数和返回值描述
**表7** PwmSetPeriod参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | PWM设备句柄 |
| period | 要设置的周期,单位为纳秒 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 |
```
```c
int32_t ret;
/* 设置周期为50000000纳秒 */
ret = PwmSetPeriod(handle, 50000000);
if (ret != 0) {
/*错误处理*/
ret = PwmSetPeriod(handle, 50000000); // 设置周期为50000000纳秒
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmSetPeriod: pwm set period failed, ret:%d\n", ret);
return ret;
}
```
#### 设置PWM设备占空时间
### 设置设备占空时间
设置PWM设备占空时间。
```
```c
int32_t PwmSetDuty(DevHandle handle, uint32_t duty);
```
**表8** PwmSetDuty参数和返回值描述
**表8** PwmSetDuty参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | PWM设备句柄 |
| duty | 要设置的占空时间,单位为纳秒 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 |
```
```c
int32_t ret;
/* 设置占空时间为25000000纳秒 */
ret = PwmSetDuty(handle, 25000000);
if (ret != 0) {
/* 错误处理 */
ret = PwmSetDuty(handle, 25000000); // 设置占空时间为25000000纳秒
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmSetDuty: pwm set duty failed, ret:%d\n", ret);
return ret;
}
```
#### 设置PWM设备极性
### 设置PWM设备极性
设置PWM设备极性。
```
```c
int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity);
```
**表9** PwmSetPolarity参数和返回值描述
**表9** PwmSetPolarity参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | PWM设备句柄 |
| polarity | 要设置的极性,正/反 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 |
```
```c
int32_t ret;
/* 设置极性为反 */
ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY);
if (ret != 0) {
/* 错误处理 */
ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); // 设置极性为反
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmSetPolarity: pwm set polarity failed, ret:%d\n", ret);
return ret;
}
```
#### 设置PWM设备参数
### 设置PWM设备参数
设置PWM设备参数。
```
```c
int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config);
```
**表10** PwmSetConfig参数和返回值描述
**表10** PwmSetConfig参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | PWM设备句柄 |
| \*config | 参数指针 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 |
```
```c
int32_t ret;
struct PwmConfig pcfg;
pcfg.duty = 25000000; /* 占空时间为25000000纳秒 */
pcfg.period = 50000000; /* 周期为50000000纳秒 */
pcfg.number = 0; /* 不断产生方波 */
pcfg.polarity = PWM_INVERTED_POLARITY; /* 极性为反 */
pcfg.status = PWM_ENABLE_STATUS; /* 运行状态为启用 */
/* 设置PWM设备参数 */
ret = PwmSetConfig(handle, &pcfg);
if (ret != 0) {
/* 错误处理 */
}
```
pcfg.duty = 25000000; // 占空时间为25000000纳秒
pcfg.period = 50000000; // 周期为50000000纳秒
pcfg.number = 0; // 不断产生方波
pcfg.polarity = PWM_INVERTED_POLARITY; // 极性为反
pcfg.status = PWM_ENABLE_STATUS; // 运行状态为启用
### 获取PWM设备参数
获取PWM设备参数。
ret = PwmSetConfig(handle, &pcfg); // 设置PWM设备参数
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmSetConfig: pwm set config failed, ret:%d\n", ret);
return ret;
}
```
#### 获取PWM设备参数
```
```c
int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config);
```
**表11** PwmGetConfig参数和返回值描述
**表11** PwmGetConfig参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | PWM设备句柄 |
| \*config | 参数指针 |
| **返回值** | **返回值描述** |
| 0 | 获取成功 |
| HDF_SUCCESS | 获取成功 |
| 负数 | 获取失败 |
```
```c
int32_t ret;
struct PwmConfig pcfg;
/*获取PWM设备参数*/
ret = PwmGetConfig(handle, &pcfg);
if (ret != 0) {
/*错误处理*/
ret = PwmGetConfig(handle, &pcfg); // 获取PWM设备参数
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmGetConfig: pwm get config failed, ret:%d\n", ret);
return ret;
}
```
## 使用实例
PWM设备完整的使用示例如下所示,首先获取PWM设备句柄,然后设置设备周期、占空时间、极性,获取设备参数。使能,设置设备参数,禁用,最后销毁PWM设备句柄。
下面将基于Hi3516DV300开发板展示使用PWM完整操作,步骤主要如下:
1. 传入PWM设备号,打开PWM设备并获得PWM设备句柄。
2. 通过PWM设备句柄及待设置的周期,设置PWM设备周期。
3. 通过PWM设备句柄及待设置的占空时间,设置PWM设备占空时间。
4. 通过PWM设备句柄及待设置的极性,设置PWM设备极性。
5. 通过PWM设备句柄及待获取的设备参数,获取PWM设备参数。
6. 通过PWM设备句柄,使能PWM设备。
7. 通过PWM设备句柄及待设置的设备参数,设置PWM设备参数。
8. 通过PWM设备句柄,禁用PWM设备。
9. 通过PWM设备句柄,关闭PWM设备。
```
void PwmTestSample(void)
```c
#include "pwm_if.h" // pwm标准接口头文件
#include "hdf_log.h" // 标准日志打印头文件
static int32_t PwmTestSample(void)
{
int32_t ret;
uint32_t num;
uint32_t period
DevHandle handle = NULL;
struct PwmConfig pcfg;
pcfg.duty = 20000000; /* 占空时间为20000000纳秒 */
pcfg.period = 40000000; /* 周期为40000000纳秒 */
pcfg.number = 100; /* 生成100个方波 */
pcfg.polarity = PWM_NORMAL_POLARITY; /* 极性为正 */
pcfg.status = PWM_ENABLE_STATUS; /* 运行状态为启用 */
pcfg.duty = 20000000; // 占空时间为20000000纳秒
pcfg.period = 40000000; // 周期为40000000纳秒
pcfg.number = 100; // 生成100个方波
pcfg.polarity = PWM_NORMAL_POLARITY; // 极性为正
pcfg.status = PWM_ENABLE_STATUS; // 运行状态为启用
/* PWM设备编号,要填写实际平台上的编号 */
num = 1;
num = 1; // PWM设备编号,要填写实际平台上的编号
/* 获取PWM设备句柄 */
handle = PwmOpen(num);
handle = PwmOpen(num); // 获取PWM设备句柄
if (handle == NULL) {
HDF_LOGE("PwmOpen: failed!\n");
HDF_LOGE("PwmOpen: open pwm_%u failed!\n", num);
return;
}
/* 设置周期为50000000纳秒 */
ret = PwmSetPeriod(handle, 50000000);
if (ret != 0) {
HDF_LOGE("PwmSetPeriod: failed, ret %d\n", ret);
goto _ERR;
ret = PwmSetPeriod(handle, 50000000); // 设置周期为50000000纳秒
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmSetPeriod: pwm set period failed, ret %d\n", ret);
goto ERR;
}
/* 设置占空时间为25000000纳秒 */
ret = PwmSetDuty(handle, 25000000);
if (ret != 0) {
HDF_LOGE("PwmSetDuty: failed, ret %d\n", ret);
goto _ERR;
ret = PwmSetDuty(handle, 25000000); // 设置占空时间为25000000纳秒
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmSetDuty: pwm set duty failed, ret %d\n", ret);
goto ERR;
}
/* 设置极性为反 */
ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY);
if (ret != 0) {
HDF_LOGE("PwmSetPolarity: failed, ret %d\n", ret);
goto _ERR;
ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); // 设置极性为反
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmSetPolarity: pwm set polarity failed, ret %d\n", ret);
goto ERR;
}
/* 获取PWM设备参数 */
ret = PwmGetConfig(handle, &pcfg);
if (ret != 0) {
HDF_LOGE("PwmGetConfig: failed, ret %d\n", ret);
goto _ERR;
ret = PwmGetConfig(handle, &pcfg); // 获取PWM设备参数
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmGetConfig: get pwm config failed, ret %d\n", ret);
goto ERR;
}
/* 启用PWM设备 */
ret = PwmEnable(handle);
if (ret != 0) {
HDF_LOGE("PwmEnable: failed, ret %d\n", ret);
goto _ERR;
ret = PwmEnable(handle); // 启用PWM设备
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmEnable: enable pwm failed, ret %d\n", ret);
goto ERR;
}
/* 设置PWM设备参数 */
ret = PwmSetConfig(handle, &pcfg);
if (ret != 0) {
HDF_LOGE("PwmSetConfig: failed, ret %d\n", ret);
goto _ERR;
ret = PwmSetConfig(handle, &pcfg); // 设置PWM设备参数
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmSetConfig: set pwm config failed, ret %d\n", ret);
goto ERR;
}
/* 禁用PWM设备 */
ret = PwmDisable(handle);
if (ret != 0) {
HDF_LOGE("PwmDisable: failed, ret %d\n", ret);
goto _ERR;
ret = PwmDisable(handle); // 禁用PWM设备
if (ret != HDF_SUCCESS) {
HDF_LOGE("PwmDisable: disable pwm failed, ret %d\n", ret);
goto ERR;
}
_ERR:
/* 销毁PWM设备句柄 */
PwmClose(handle);
ERR:
PwmClose(handle); // 销毁PWM设备句柄
return ret;
}
```
# Watchdog
## 概述
看门狗(Watchdog),又叫看门狗计时器(Watchdog timer),是一种硬件的计时设备。当系统主程序发生错误导致未及时清除看门狗计时器的计时值时,看门狗计时器就会对系统发出复位信号,使系统从悬停状态恢复到正常运作状态。
### 功能简介
看门狗(Watchdog),又称看门狗计时器(Watchdog timer),是一种硬件计时设备。一般有一个输入,叫做喂狗,一个输出到系统的复位端。当系统主程序发生错误导致未及时清除看门狗计时器的计时值时,看门狗计时器就会对系统发出复位信号,使系统从悬停状态恢复到正常运作状态。
## 接口说明
Watchdog接口定义了看门狗操作的通用方法集合,包括:
**表1** 看门狗API接口功能介绍
- 打开/关闭看门狗设备
- 启动/停止看门狗设备
- 设置/获取看门狗设备超时时间
- 获取看门狗设备状态
- 喂狗
| 接口 | 接口描述 |
| -------- | -------- |
| WatchdogOpen | 打开看门狗 |
| WatchdogClose | 关闭看门狗 |
| WatchdogStart | 启动看门狗 |
| WatchdogStop | 停止看门狗 |
| WatchdogSetTimeout | 设置看门狗超时时间 |
| WatchdogGetTimeout | 获取看门狗超时时间 |
| WatchdogGetStatus | 获取看门狗状态 |
| WatchdogFeed | 清除看门狗定时器(喂狗) |
### 基本概念
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
> 本文涉及的看门狗的所有接口,仅限内核态使用,不支持在用户态使用。
系统正常工作的时候,每隔一段时间输出一个信号到喂狗端,给看门狗清零,这个操作就叫做喂狗。如果超过规定的时间不喂狗,看门狗定时超时,就会给出一个复位信号到系统,使系统复位。
### 运作机制
在HDF框架中,Watchdog模块接口适配模式采用独立服务模式,如图1所示。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDF设备管理器的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为:
- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。
- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。
Watchdog模块各分层作用:
- 接口层提供打开看门狗设备、获取看门狗设备状态、启动看门狗设备、设置看门狗设备超时时间、获取看门狗设备超时时间、喂狗、停止看门狗设备超时时间、关闭看门狗设备的接口。
- 核心层主要提供看门狗控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。
- 适配层主要是将钩子函数的功能实例化,实现具体的功能。
**图 1** Watchdog独立服务模式结构图
![image1](figures/独立服务模式结构图.png "Watchdog独立服务模式结构图")
## 使用指导
### 场景介绍
### 使用流程
对于无法直接观测到的软件异常,我们可以使用看门狗进行自动检测,并在异常产生时及时重置。
使用看门狗的一般流程如下图所示。
### 接口说明
**图1** 看门狗使用流程图
Watchdog模块提供的主要接口如表1所示。
![image](figures/看门狗使用流程图.png "看门狗使用流程图")
**表1** 看门狗API接口功能介绍
| 接口名 | 描述 |
| -------- | -------- |
| int32_t WatchdogOpen(int16_t wdtId, DevHandle *handle) | 打开看门狗 |
| void WatchdogClose(DevHandle handle) | 关闭看门狗 |
| int32_t WatchdogStart(DevHandle handle) | 启动看门狗 |
| int32_t WatchdogStop(DevHandle handle) | 停止看门狗 |
| int32_t WatchdogSetTimeout(DevHandle handle, uint32_t seconds) | 设置看门狗超时时间 |
| int32_t WatchdogGetTimeout(DevHandle handle, uint32_t *seconds) | 获取看门狗超时时间 |
| int32_t WatchdogGetStatus(DevHandle handle, int32_t *status) | 获取看门狗状态 |
| int32_t WatchdogFeed(DevHandle handle) | 清除看门狗定时器(喂狗) |
### 打开看门狗设备
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
> 本文涉及的看门狗的所有接口,支持内核态及用户态使用。
在操作看门狗之前,需要使用WatchdogOpen打开一个看门狗设备,一个系统可能有多个看门狗,通过ID号来打开指定的看门狗设备:
### 开发步骤
使用看门狗的一般流程如下图所示。
```
DevHandle WatchdogOpen(int16_t wdtId);
**图2** 看门狗使用流程图
![image2](figures/看门狗使用流程图.png "看门狗使用流程图")
#### 打开看门狗设备
在操作看门狗之前,需要调用WatchdogOpen打开看门狗设备,一个系统可能有多个看门狗,通过看门狗ID号来打开指定的看门狗设备:
```c
DevHandle WatchdogOpen(int16_t wdtId, DevHandle *handle);
```
**表2** WatchdogOpen参数和返回值描述
**表2** WatchdogOpen参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| wdtId | 看门狗设备号 |
| handle | 看门狗设备句柄 |
| **返回值** | **返回值描述** |
| NULL | 打开失败 |
| DevHandle类型指针 | 看门狗设备句柄 |
| HDF_SUCCESS | 打开看门狗设备成功 |
| 负数 | 打开看门狗设备失败 |
```c
int16_t wdtId = 0;
int32_t ret;
DevHandle *handle = NULL;
```
DevHandle handle = NULL;
handle = WatchdogOpen(0); /* 打开0号看门狗设备 */
if (handle == NULL) {
HDF_LOGE("WatchdogOpen: failed, ret %d\n", ret);
return;
ret = WatchdogOpen(wdtId, handle); // 打开0号看门狗设备
if (ret != HDF_SUCCESS) {
HDF_LOGE("WatchdogOpen: open watchdog_%hd failed, ret:%d\n", wdtId, ret);
return ret;
}
```
#### 获取看门狗状态
### 获取看门狗状态
```
```c
int32_t WatchdogGetStatus(DevHandle handle, int32_t *status);
```
**表3** WatchdogGetStatus参数和返回值描述
**表3** WatchdogGetStatus参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | 看门狗设备句柄 |
| status | 获取到的看门狗状态的指针 |
| status | 获取到的看门狗状态 |
| **返回值** | **返回值描述** |
| 0 | 获取成功 |
| 负数 | 获取失败 |
| HDF_SUCCESS | 获取看门狗状态成功 |
| 负数 | 获取看门狗状态失败 |
```
```c
int32_t ret;
int32_t status;
/* 获取Watchdog启动状态 */
ret = WatchdogGetStatus(handle, &status);
if (ret != 0) {
HDF_LOGE("WatchdogGetStatus: failed, ret %d\n", ret);
return;
ret = WatchdogGetStatus(handle, &status); // 获取Watchdog状态
if (ret != HDF_SUCCESS) {
HDF_LOGE("WatchdogGetStatus: watchdog get status failed, ret:%d\n", ret);
return ret;
}
```
### 设置超时时间
#### 设置超时时间
```
```c
int32_t WatchdogSetTimeout(DevHandle *handle, uint32_t seconds);
```
**表4** WatchdogSetTimeout参数和返回值描述
**表4** WatchdogSetTimeout参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | 看门狗设备句柄 |
| seconds | 超时时间,单位为秒 |
| **返回值** | **返回值描述** |
| 0 | 设置成功 |
| HDF_SUCCESS | 设置成功 |
| 负数 | 设置失败 |
```
```c
int32_t ret;
uint32_t timeOut = 60;
/* 设置超时时间,单位:秒 */
ret = WatchdogSetTimeout(handle, timeOut);
if (ret != 0) {
HDF_LOGE("WatchdogSetTimeout: failed, ret %d\n", ret);
return;
ret = WatchdogSetTimeout(handle, 2); // 设置超时时间2秒
if (ret != HDF_SUCCESS) {
HDF_LOGE("WatchdogSetTimeout: watchdog set timeOut failed, ret:%d\n", ret);
return ret;
}
```
#### 获取超时时间
### 获取超时时间
```
```c
int32_t WatchdogGetTimeout(DevHandle *handle, uint32_t *seconds);
```
**表5** WatchdogGetTimeout参数和返回值描述
**表5** WatchdogGetTimeout参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | 看门狗设备句柄 |
| seconds | 接收超时时间的指针,单位为秒 |
| seconds | 获取的看门狗超时时间 |
| **返回值** | **返回值描述** |
| 0 | 获取成功 |
| 负数 | 获取失败 |
| HDF_SUCCESS | 获取看门狗超时时间成功 |
| 负数 | 获取看门狗超时时间失败 |
```c
int32_t ret;
uint32_t timeOut;
ret = WatchdogGetTimeout(handle, &timeOut); // 获取超时时间
if (ret != HDF_SUCCESS) {
HDF_LOGE("WatchdogGetTimeout: watchdog get timeOut failed, ret:%d\n", ret);
return ret;
}
```
int32_t ret;
uint32_t timeOut;
/* 获取超时时间,单位:秒 */
ret = WatchdogGetTimeout(handle, &timeOut);
if (ret != 0) {
HDF_LOGE("WatchdogGetTimeout: failed, ret %d\n", ret);
return;
}
```
### 启动看门狗
#### 启动看门狗
```
```c
int32_t WatchdogStart(DevHandle handle);
```
**表6** WatchdogStart参数和返回值描述
**表6** WatchdogStart参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | 看门狗设备句柄 |
| **返回值** | **返回值描述** |
| 0 | 启动成功 |
| 负数 | 启动失败 |
| HDF_SUCCESS | 启动看门狗成功 |
| 负数 | 启动看门狗失败 |
```
```c
int32_t ret;
/* 启动看门狗 */
ret = WatchdogStart(handle);
if (ret != 0) {
HDF_LOGE("WatchdogStart: failed, ret %d\n", ret);
return;
ret = WatchdogStart(handle); // 启动看门狗
if (ret != HDF_SUCCESS) {
HDF_LOGE("WatchdogStart: start watchdog failed, ret:%d\n", ret);
return ret;
}
```
#### 喂狗
### 喂狗
```
```c
int32_t WatchdogFeed(DevHandle handle);
```
**表7** WatchdogFeed参数和返回值描述
**表7** WatchdogFeed参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | 看门狗设备句柄 |
| **返回值** | **返回值描述** |
| 0 | 喂狗成功 |
| HDF_SUCCESS | 喂狗成功 |
| 负数 | 喂狗失败 |
```
```c
int32_t ret;
/* 喂狗 */
ret = WatchdogFeed(handle);
if (ret != 0) {
HDF_LOGE("WatchdogFeed: failed, ret %d\n", ret);
return;
ret = WatchdogFeed(handle); // 喂狗
if (ret != HDF_SUCCESS) {
HDF_LOGE("WatchdogFeed: feed watchdog failed, ret:%d\n", ret);
return ret;
}
```
#### 停止看门狗
### 停止看门狗
```
```c
int32_t WatchdogStop(DevHandle handle);
```
**表8** WatchdogStop参数和返回值描述
**表8** WatchdogStop参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | 看门狗设备句柄 |
| **返回值** | **返回值描述** |
| 0 | 停止成功 |
| 负数 | 停止失败 |
| HDF_SUCCESS | 停止看门狗成功 |
| 负数 | 停止看门狗失败 |
```
```c
int32_t ret;
/* 停止看门狗 */
ret = WatchdogStop(handle);
if (ret != 0) {
HDF_LOGE("WatchdogStop: failed, ret %d\n", ret);
return;
ret = WatchdogStop(handle); // 停止看门狗
if (ret != HDF_SUCCESS) {
HDF_LOGE("WatchdogStop: stop watchdog failed, ret:%d\n", ret);
return ret;
}
```
#### 关闭看门狗设备
### 关闭看门狗设备
当操作完毕时,使用WatchdogClose关闭打开的设备句柄:
当所有操作完毕后,调用WatchdogClose关闭打开的看门狗设备:
```
```c
void WatchdogClose(DevHandle handle);
```
**表9** WatchdogClose参数和返回值描述
**表9** WatchdogClose参数和返回值描述
| **参数** | **参数描述** |
| -------- | -------- |
| handle | 看门狗设备句柄 |
```
/* 关闭看门狗 */
ret = WatchdogClose(handle);
```c
WatchdogClose(handle); // 关闭看门狗
```
## 使用实例
本例程提供看门狗的完整使用流程。
在本例程中,我们打开一个看门狗设备,设置超时时间并启动计时:
- 首先定期喂狗,即按时清除看门狗定时器,确保系统不会因定时器超时而复位。
下面将基于Hi3516DV300开发板展示使用Watchdog完整操作,步骤主要如下:
- 接着再停止喂狗,观察定时器到期后系统是否发生复位行为。
1. 传入看门狗ID号,及空的描述句柄,打开看门狗设备并获得看门狗设备句柄。
2. 通过看门狗设备句柄及超时时间,设置看门狗设备超时时间。
3. 通过看门狗设备句柄及待获取超时时间,获取看门狗设备超时时间。
4. 通过看门狗设备句柄启动看门狗设备。
5. 通过看门狗设备句柄喂狗。
6. 通过看门狗设备句柄停止看门狗设备。
7. 通过看门狗设备句柄关闭看门狗设备。
示例如下:
```
#include "watchdog_if.h"
#include "hdf_log.h"
#include "osal_irq.h"
#include "osal_time.h"
```c
#include "watchdog_if.h" /* watchdog标准接口头文件 */
#include "hdf_log.h" /* 标准日志打印头文件 */
#include "osal_time.h" /* 标准延迟&睡眠接口头文件 */
#define WATCHDOG_TEST_TIMEOUT 2
#define WATCHDOG_TEST_FEED_TIME 6
......@@ -287,14 +299,16 @@ static int32_t TestCaseWatchdog(void)
{
int32_t i;
int32_t ret;
int16_t wdtId = 0;
int32_t status;
uint32_t timeout;
DevHandle handle = NULL;
DevHandle *handle = NULL;
/* 打开0号看门狗设备 */
handle = WatchdogOpen(0);
if (handle == NULL) {
HDF_LOGE("Open watchdog failed!");
return -1;
ret = WatchdogOpen(wdtId, handle);
if (ret != HDF_SUCCESS) {
HDF_LOGE("WatchdogOpen: open watchdog_%hd failed, ret:%d\n", wdtId, ret);
return ret;
}
/* 设置超时时间 */
......@@ -305,13 +319,19 @@ static int32_t TestCaseWatchdog(void)
return ret;
}
/* 回读设置的超时时间值 */
/* 获取超时时间 */
ret = WatchdogGetTimeout(handle, &timeout);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: get timeout fail! ret:%d\n", __func__, ret);
WatchdogClose(handle);
return ret;
}
/* 比较设置与获取的超时时间是否一致*/
if (timeout != WATCHDOG_TEST_TIMEOUT) {
HDF_LOGE("%s: set:%u, but get:%u", __func__, WATCHDOG_TEST_TIMEOUT, timeout);
WatchdogClose(handle);
return HDF_FAILURE;
}
HDF_LOGI("%s: read timeout back:%u\n", __func__, timeout);
/* 启动看门狗,开始计时 */
......@@ -321,6 +341,19 @@ static int32_t TestCaseWatchdog(void)
WatchdogClose(handle);
return ret;
}
/* 获取看门狗状态,是否启动*/
status = WATCHDOG_STOP;
ret = WatchdogGetStatus(handle, &status);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: get status fail! ret:%d", __func__, ret);
WatchdogClose(handle);
return ret;
}
if (status != WATCHDOG_START) {
HDF_LOGE("%s: status is:%d after start", __func__, status);
WatchdogClose(handle);
return HDF_FAILURE;
}
/* 每隔1S喂狗一次 */
for (i = 0; i < WATCHDOG_TEST_FEED_TIME; i++) {
......@@ -336,15 +369,26 @@ static int32_t TestCaseWatchdog(void)
/* 由于喂狗间隔小于超时时间,系统不会发生复位,此日志可以正常打印 */
HDF_LOGI("%s: no reset ... feeding test OK!!!\n", __func__);
/* 接下来持续不喂狗,使得看门狗计时器超时 */
for (i = 0; i < WATCHDOG_TEST_FEED_TIME; i++) {
HDF_LOGI("%s: waiting dog buck %d times... \n", __func__, i);
OsalSleep(1);
ret = WatchdogStop(handle);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: stop fail! ret:%d", __func__, ret);
WatchdogClose(handle);
return ret;
}
/* 获取看门狗状态,是否停止*/
status = WATCHDOG_START;
ret = WatchdogGetStatus(handle, &status);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%s: get status fail! ret:%d", __func__, ret);
WatchdogClose(handle);
return ret;
}
if (status != WATCHDOG_STOP) {
HDF_LOGE("%s: status is:%d after stop", __func__, status);
WatchdogClose(handle);
return HDF_FAILURE;
}
/* 当不喂狗时间到达之前设定的超时时间的时候,系统会发生复位,理论上观察不到此日志的打印 */
HDF_LOGI("%s: dog hasn't back!!! \n", __func__, i);
WatchdogClose(handle);
return -1;
return HDF_SUCCESS;
}
```
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册