1.PIN设备新增外部中断独立回调函数。

2.新增PIN设备使用文档。
This commit is contained in:
MacRsh
2023-12-05 00:37:13 +08:00
parent 47578034ea
commit d73b36edf6
5 changed files with 378 additions and 10 deletions

View File

@@ -18,7 +18,7 @@
* [添加包含路径](#添加包含路径)
* [先来点个灯吧](#先来点个灯吧)
* [Hello World](#hello-world)
* [现在您已经完成了入门基础,开始进一步探索MR库吧](#现在您已经完成了入门基础开始进一步探索mr库吧)
* [现在您已经完成了入门教程,开始使用MR库吧](#现在您已经完成了入门教程开始使用mr库吧)
<!-- TOC -->
----------
@@ -229,9 +229,9 @@ int main(void)
----------
# 现在您已经完成了入门基础,开始进一步探索MR库吧
# 现在您已经完成了入门教程,开始使用MR库吧
1. 欢迎参考更多教程,文档目录在document
2. 可以尝试基于某款芯片开发驱动,练习设备驱动编程。
1. 参考更多教程:目录 `document`
2. 尝试基于某款芯片开发驱动,练习设备驱动编程。
3. 尝试编写更多设备模板和开发更多功能。
4. 欢迎您提出意见和建议。如果您对开发有兴趣,诚邀您参与到 `MR` 项目的开发中来项目交流群199915649QQ

View File

@@ -18,7 +18,7 @@
* [Add Include Paths](#add-include-paths)
* [Let's Light an LED](#lets-light-an-led)
* [Hello World](#hello-world)
* [Now you have completed the basics, start exploring the MR library further.](#now-you-have-completed-the-basics-start-exploring-the-mr-library-further)
* [Now that you have completed the introductory tutorial, start using the MR Library.](#now-that-you-have-completed-the-introductory-tutorial-start-using-the-mr-library)
<!-- TOC -->
----------
@@ -246,10 +246,10 @@ int main(void)
----------
# Now you have completed the basics, start exploring the MR library further.
# Now that you have completed the introductory tutorial, start using the MR Library.
1. Welcome to refer to more tutorials, document directory is in document.
2. You can try developing drivers based on certain chips to practice device driver programming.
1. See more tutorials: directory `document`.
2. Try developing drivers based on certain chips to practice device driver programming.
3. Try writing more device templates and developing more features.
4. Welcome to provide your opinions and suggestions. If you are interested in development, you are welcome to
participate in the development of the `MR` project. The project discussion group is: 199915649(QQ).

View File

@@ -10,6 +10,14 @@
#ifdef MR_USING_PIN
struct pin_irq
{
struct mr_list list;
int number;
int desc;
int (*call)(int desc, void *args);
};
static ssize_t mr_pin_read(struct mr_dev *dev, int off, void *buf, size_t size, int async)
{
struct mr_pin *pin = (struct mr_pin *)dev;
@@ -71,7 +79,49 @@ static int mr_pin_ioctl(struct mr_dev *dev, int off, int cmd, void *args)
return MR_EINVAL;
}
return ops->configure(pin, off, mode);
/* Configure pin mode */
int ret = ops->configure(pin, off, mode);
if (ret != MR_EOK)
{
return ret;
}
/* If the irq exists, update it */
struct mr_list *list = MR_NULL;
for (list = pin->irq_list.next; list != &pin->irq_list; list = list->next)
{
struct pin_irq *irq = (struct pin_irq *)mr_container_of(list, struct pin_irq, list);
if (irq->number == off)
{
if (mode < MR_PIN_MODE_IRQ_RISING)
{
/* Remove irq */
mr_list_remove(list);
mr_free(irq);
} else
{
/* Update irq */
irq->desc = pin->dev.rd_call.desc;
irq->call = pin->dev.rd_call.call;
}
return MR_EOK;
}
}
/* If not exist, allocate new irq */
if (mode >= MR_PIN_MODE_IRQ_RISING)
{
struct pin_irq *irq = (struct pin_irq *)mr_malloc(sizeof(struct pin_irq));
if (irq != MR_NULL)
{
mr_list_init(&irq->list);
irq->number = off;
irq->desc = dev->rd_call.desc;
irq->call = dev->rd_call.call;
mr_list_insert_before(&pin->irq_list, &irq->list);
}
}
return MR_EOK;
}
return MR_EINVAL;
}
@@ -85,11 +135,26 @@ static int mr_pin_ioctl(struct mr_dev *dev, int off, int cmd, void *args)
static ssize_t mr_pin_isr(struct mr_dev *dev, int event, void *args)
{
struct mr_pin *pin = (struct mr_pin *)dev;
switch (event)
{
case MR_ISR_PIN_RD_INT:
{
return (ssize_t)*(int *)args;
ssize_t number = *(int *)args;
/* If the irq exists, call it */
struct mr_list *list = MR_NULL;
for (list = pin->irq_list.next; list != &pin->irq_list; list = list->next)
{
struct pin_irq *irq = (struct pin_irq *)mr_container_of(list, struct pin_irq, list);
if (irq->number == number)
{
irq->call(irq->desc, &number);
return MR_EEXIST;
}
}
return number;
}
default:
@@ -125,6 +190,9 @@ int mr_pin_register(struct mr_pin *pin, const char *name, struct mr_drv *drv)
mr_assert(drv != MR_NULL);
mr_assert(drv->ops != MR_NULL);
/* Initialize the fields */
mr_list_init(&pin->irq_list);
/* Register the pin */
return mr_dev_register(&pin->dev, name, Mr_Dev_Type_Pin, MR_SFLAG_RDWR, &ops, drv);
}

298
document/device/pin.md Normal file
View File

@@ -0,0 +1,298 @@
# PIN设备
<!-- TOC -->
* [PIN设备](#pin设备)
* [打开PIN设备](#打开pin设备)
* [关闭PIN设备](#关闭pin设备)
* [配置PIN设备](#配置pin设备)
* [设置/获取引脚编号](#设置获取引脚编号)
* [引脚编号](#引脚编号)
* [设置引脚模式](#设置引脚模式)
* [引脚模式](#引脚模式)
* [设置/获取外部中断回调函数](#设置获取外部中断回调函数)
* [读取PIN设备引脚电平](#读取pin设备引脚电平)
* [写入PIN设备引脚电平](#写入pin设备引脚电平)
* [使用示例:](#使用示例)
<!-- TOC -->
## 打开PIN设备
```c
int mr_dev_open(const char *name, int oflags);
```
| 参数 | 描述 |
|---------|---------|
| name | 设备名称 |
| oflags | 打开设备的标志 |
| **返回值** | |
| `>=0` | 设备描述符 |
| `<0` | 错误码 |
- `name`PIN设备名称一般为`pin`
- `oflags`:打开设备的标志,支持 `MR_OFLAG_RDONLY``MR_OFLAG_WRONLY``MR_OFLAG_RDWR`
使用时应根据实际情况为不同的任务分别打开PIN设备并使用适当的`oflags`进行管理和权限控制,以确保它们不会相互影响。
## 关闭PIN设备
```c
int mr_dev_close(int desc);
```
| 参数 | 描述 |
|---------|-------|
| desc | 设备描述符 |
| **返回值** | |
| `=0` | 关闭成功 |
| `<0` | 错误码 |
注:关闭设备时并不会将之前的配置恢复到默认状态,需要用户自行根据实际情况进行恢复。
## 配置PIN设备
```c
int mr_dev_ioctl(int desc, int cmd, void *args);
```
| 参数 | 描述 |
|---------|-------|
| desc | 设备描述符 |
| cmd | 命令码 |
| args | 命令参数 |
| **返回值** | |
| `=0` | 设置成功 |
| `<0` | 错误码 |
- `cmd`:命令码,支持以下命令:
- `MR_CTL_PIN_SET_NUMBER`:设置引脚编号。
- `MR_CTL_PIN_GET_NUMBER`:获取引脚编号。
- `MR_CTL_PIN_SET_MODE`:设置引脚模式。
- `MR_CTL_PIN_SET_EXTI_CALL`:设置外部中断回调函数。
- `MR_CTL_PIN_GET_EXTI_CALL`:获取外部中断回调函数。
### 设置/获取引脚编号
#### 引脚编号
不同 `MCU``GPIO` 数量、功能、命名规则等往往不同,所以 `MR` 使用数字的方式来定义 `GPIO` 引脚,以便在不同 `MCU` 上使用统一的接口。
默认计算公式为:`Number = Port * 16 + Pin`,其中 `Port``GPIO` 端口编号,`Pin``GPIO` 引脚编号。
例如 `PC13` 对应的 `Port``C``Pin``13`,则 `Number = (C - A * 16) + 13 = 32 + 13 = 45`
注:此规则并不适用于所有 `MCU`,对于特殊的需求需要查看底层驱动设定。
```c
/* 定义引脚编号 */
#define
PIN_NUMBER 45
/* 设置引脚编号 */
mr_dev_ioctl(ds, MR_CTL_PIN_SET_NUMBER, mr_make_local(int, PIN_NUMBER));
/* 获取引脚编号 */
int number;
mr_dev_ioctl(ds, MR_CTL_PIN_GET_NUMBER, &number);
```
### 设置引脚模式
#### 引脚模式
`MR` 支持6种普通模式
- `MR_PIN_MODE_NONE`:无模式,引脚恢复默认状态。
- `MR_PIN_MODE_OUTPUT`:输出模式,引脚为推挽输出模式。
- `MR_PIN_MODE_OUTPUT_OD`:输出模式,引脚为开漏输出模式。
- `MR_PIN_MODE_INPUT`:输入模式,引脚为浮空输入模式。
- `MR_PIN_MODE_INPUT_DOWN`:输入模式,引脚为下拉输入模式。
- `MR_PIN_MODE_INPUT_UP`:输入模式,引脚为上拉输入模式。
以及5种外部中断模式
- `MR_PIN_MODE_IRQ_RISING`:外部中断模式,上升沿触发中断。
- `MR_PIN_MODE_IRQ_FALLING`:外部中断模式,下降沿触发中断。
- `MR_PIN_MODE_IRQ_EDGE`:外部中断模式,上升沿和下降沿触发中断。
- `MR_PIN_MODE_IRQ_LOW`:外部中断模式,低电平触发中断。
- `MR_PIN_MODE_IRQ_HIGH`:外部中断模式,高电平触发中断。
```c
/* 定义引脚模式 */
#define
PIN_MODE MR_PIN_MODE_OUTPUT
/* 设置引脚模式 */
mr_dev_ioctl(ds, MR_CTL_PIN_SET_MODE, mr_make_local(int, PIN_MODE));
```
### 设置/获取外部中断回调函数
```c
#define PIN_NUMBER 45
/* 定义外部中断回调函数 */
int call(int desc, void *args)
{
/* 获取引脚编号 */
ssize_t number = *(ssize_t *)args;
/* 处理外部中断事件 */
return MR_EOK;
}
/* 设置外部中断回调函数 */
mr_dev_ioctl(ds, MR_CTL_PIN_SET_EXTI_CALL, call);
/* 获取外部中断回调函数 */
int (*callback)(int, void *args);
mr_dev_ioctl(ds, MR_CTL_PIN_GET_EXTI_CALL, &callback);
```
注:
-
在设置回调函数之后,需要重新配置一次引脚模式为外部中断模式才能使回调函数绑定到对应引脚。因此在配置引脚模式之前,请先设置回调函数,且回调函数中一定要判断引脚源是否正确(未绑定的情况下,若同样有配置为外部中断模式的但未绑定引脚触发了外部中断,此时将调用未绑定的回调函数)。
- 如果设置外部中断时没有重新配置回调函数,则默认使用上一次的结果,并且不会使用当前设备描述符。
- 即使PIN设备被关闭回调函数也不会失效直至引脚被设置为普通模式PIN设备关闭时外部中断将被忽略
## 读取PIN设备引脚电平
```c
ssize_t mr_dev_read(int desc, void *buf, size_t size);
```
| 参数 | 描述 |
|---------|---------|
| desc | 设备描述符 |
| buf | 读取数据缓冲区 |
| size | 读取数据大小 |
| **返回值** | |
| `>=0` | 读取数据大小 |
| `<0` | 错误码 |
```c
/* 读取引脚电平 */
uint8_t pin_level;
int ret = mr_dev_read(ds, &pin_level, sizeof(pin_level));
/* 是否读取成功 */
if (ret != sizeof(pin_level))
{
return ret;
}
```
## 写入PIN设备引脚电平
```c
ssize_t mr_dev_write(int desc, const void *buf, size_t size);
```
| 参数 | 描述 |
|---------|---------|
| desc | 设备描述符 |
| buf | 写入数据缓冲区 |
| size | 写入数据大小 |
| **返回值** | |
| `>=0` | 写入数据大小 |
| `<0` | 错误码 |
- 电平:`0`:低电平;`1`:高电平,推荐使用宏:`MR_PIN_LOW_LEVEL``MR_PIN_HIGH_LEVEL`
```c
/* 写入引脚电平 */
uint8_t pin_level = MR_PIN_HIGH_LEVEL;
int ret = mr_dev_write(ds, &pin_level, sizeof(pin_level));
/* 是否写入成功 */
if (ret != sizeof(pin_level))
{
return ret;
}
```
## 使用示例:
```c
#include "include/mr_lib.h"
/* 定义LED引脚PC13和KEY引脚PA0*/
#define LED_PIN_NUMBER 45
#define KEY_PIN_NUMBER 0
int key_call(int desc, void *args)
{
ssize_t number = *((ssize_t *)args);
if (number == KEY_PIN_NUMBER)
{
/* 打印回调函数描述符 */
mr_printf("KEY callback, desc: %d\r\n", desc);
/* 翻转LED引脚电平 */
uint8_t level = 0;
mr_dev_read(desc, &level, sizeof(level));
level = !level;
mr_dev_write(desc, &level, sizeof(level));
}
}
int led_key_init(void)
{
int ret = MR_EOK;
/* 初始化LED */
int led_ds = mr_dev_open("pin", MR_OFLAG_RDWR);
if (led_ds < 0)
{
mr_printf("led open failed: %s\r\n", mr_strerror(led_ds));
return led_ds;
}
/* 打印LED描述符 */
mr_printf("LED desc: %d\r\n", led_ds);
/* 设置到LED引脚 */
mr_dev_ioctl(led_ds, MR_CTL_PIN_SET_NUMBER, mr_make_local(int, LED_PIN_NUMBER));
/* 设置LED引脚为推挽输出模式 */
ret = mr_dev_ioctl(led_ds, MR_CTL_PIN_SET_MODE, mr_make_local(int, MR_PIN_MODE_OUTPUT));
if (ret < 0)
{
mr_printf("led set mode failed: %s\r\n", mr_strerror(ret));
return ret;
}
/* 设置KEY外部中断回调函数为了演示描述符的继承机制此处使用LED设备描述符配置KEY回调函数 */
mr_dev_ioctl(led_ds, MR_CTL_PIN_SET_EXTI_CALL, key_call);
/* 初始化KEY */
int key_ds = mr_dev_open("pin", MR_OFLAG_RDWR);
if (key_ds < 0)
{
mr_printf("key open failed: %s\r\n", mr_strerror(key_ds));
return key_ds;
}
/* 打印KEY描述符 */
mr_printf("KEY desc: %d\r\n", key_ds);
/* 设置到KEY引脚 */
mr_dev_ioctl(key_ds, MR_CTL_PIN_SET_NUMBER, mr_make_local(int, KEY_PIN_NUMBER));
/* 设置KEY引脚为外部中断下降沿模式未重新配置回调函数则使用上一次的结果即LED设置的回调函数 */
ret = mr_dev_ioctl(key_ds, MR_CTL_PIN_SET_MODE, mr_make_local(int, MR_PIN_MODE_IRQ_FALLING));
if (ret < 0)
{
mr_printf("key set mode failed: %s\r\n", mr_strerror(ret));
return ret;
}
return MR_EOK;
}
/* 导出到自动初始化APP级 */
MR_APP_EXPORT(led_key_init);
int main(void)
{
/* 自动初始化led_key_init函数将在此处自动调用 */
mr_auto_init();
while(1)
{
}
}
```
按下KEY后LED将翻转。
观察串口打印可以看到LED和KEY的描述符。虽然KEY引脚的外部中断是KEY配置的但未配置回调函数。故KEY引脚回调函数继承之前的配置即LED配置的回调函数所以KEY回调函数中打印的描述符为LED的描述符。

View File

@@ -67,6 +67,8 @@ typedef uint8_t mr_pin_data_t; /**< PIN rea
struct mr_pin
{
struct mr_dev dev; /**< Device */
struct mr_list irq_list; /**< IRQ list */
};
/**