打印
[单片机芯片]

mr-library开源项目

[复制链接]
2831|17
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
MacRsh|  楼主 | 2023-6-7 19:02 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
# 背景

随着国产微控制器的崛起,市场上的微控制器种类越来越多。然而,以前的微控制器开发往往忽略了整体框架和程序分层,导致更换微控制器型号往往需要更改应用层代码,这使得开发工作变得繁重且乏味。常见的开发方式大多分为两种:
常见的开发方式通常分为两种:裸机编程和RTOS编程,由于两种方式的代码编写方式存在巨大差异,因此在两种方式之间切换意味着需要进行大规模的工程修改。
**mr-library** 的目标是帮助开发者提高开发效率和代码通用性,降低平台迁移的难度。

----------

# mr-library 简介

**mr-library** 是一个嵌入式软件库,完全采用C语言编写,使用面向对象的设计方法,代码框架清晰,可以快速移植到不同的平台。它包括以下部分:

* 内核层: **mr-library** 的核心部分,包含容器、对象、服务等。将各种对象注册到内核维护的容器中,使得应用更加高效有序。
* 设备框架层: 提供统一的设备接口,将不同的设备接入到内核中。在应用层,仅需调用内核设备I/O接口即可访问设备。
* 硬件驱动层: 为设备框架层设备提供必要的驱动,当硬件更换时仅修改驱动层。
* 组件层: 通过内核提供的API实现不同的功能。包括但不限于虚拟文件系统、通用传感器模块、网络框架等。
* 软件包: 可独立使用,无依赖的软件包。

----------

# 代码目录

**mr-library** 的代码目录结构如下表所示:

| 名称       | 描述    |
|:---------|:------|
| bsp      | 板级支持包 |
| device   | 设备文件  |
| document | 文档    |
| driver   | 驱动文件  |
| include  | 库头文件  |
| module   | 组件    |
| package  | 软件包   |
| src      | 库源文件  |

----------

# 内核

内核中包含了容器、对象、服务等。

## 容器

容器负责统一管理注册到内核中的对象。

### 容器原型

```c
struct mr_container
{
    struct mr_list list;                                            /* 容器链表 */

    enum mr_container_type type;                                    /* 容器类型 */
};
```

- 容器链表:所有注册到容器的对象都将链接到容器链表上,当对象被移除容器时也将从容器链表上移除。

- 容器类型:指定容器类型用以存放指定类型对象。

### 容器类型

内核维护了以下几类容器:

```c
enum mr_container_type
{
    MR_CONTAINER_TYPE_MISC,                                         /* 杂类容器 */
    MR_CONTAINER_TYPE_DEVICE,                                       /* 设备容器 */
    MR_CONTAINER_TYPE_SERVER,                                       /* 服务容器 */
};
```

## 对象

### 对象原型

```c
struct mr_object
{
    struct mr_list list;                                            /* 对象链表 */

    char name[MR_CONF_NAME_MAX + 1];                                /* 对象名 */
    mr_uint8_t flag;                                                /* 对象标志 */
};
```

- 对象链表:用于将对象注册到容器中。

- 对象名:对象的名称,同一容器不允许出现同名对象,不同容器允许对象重名。

- 对象标志:用于标记对象状态。

### 对象操作接口

| 接口               | 描述        |
|:-----------------|:----------|
| mr_object_find   | 从内核容器查找对象 |
| mr_object_add    | 添加对象到内核容器 |
| mr_object_remove | 从内核容器移除对象 |
| mr_object_move   | 移动对象      |
| mr_object_rename | 重命名对象     |

## 服务

### 事件服务

事件服务器是一种异步事件处理机制,它通过事件分发和回调的方式,可以有效地提高系统的异步处理能力、解耦性和可扩展性。

事件服务器包含两个主要组件:事件服务器和事件客户端。
- 事件服务器用于接收和分发事件,它内部维护一个事件队列用于存储待处理事件和一个事件列表用于存储注册的事件客户端。
- 事件客户端用于处理特定类型的事件,它需要注册到事件服务器并提供一个回调函数。

当事件发生时,事件服务器会将事件插入到其事件队列中进行缓存。事件服务器会周期性地从事件队列中取出事件进行分发,找到对应的事件客户端,然后调用其注册的回调函数进行事件处理。

### 事件服务原型

```c
/* 事件服务器 */
struct mr_event_server
{
    struct mr_object object;                                        /**< 事件服务对象 */

    struct mr_fifo queue;                                           /**< 事件队列 */
    mr_avl_t list;                                                  /**< 事件链表 */
};

/* 事件客户端 */
struct mr_event_client
{
    struct mr_avl list;                                             /**< 事件链表 */

    mr_err_t (*cb)(mr_event_server_t server, void *args);           /**< 事件回调函数 */
    void *args;                                                     /**< 事件回调函数参数 */
};
```

### 事件服务操作接口

| 接口                       | 描述               |
|:-------------------------|:-----------------|
| mr_event_server_find     | 从内核容器查找事件服务器     |
| mr_event_server_add      | 添加事件服务器到内核容器     |
| mr_event_server_remove   | 从内核容器移除事件服务器     |
| mr_event_server_notify   | 通知事件服务器事件发生      |
| mr_event_server_handle   | 事件服务器分发事件        |
| mr_event_client_find     | 从事件服务器查找事件客户端    |
| mr_event_client_create   | 创建事件客户端到事件服务器    |
| mr_client_delete         | 从事件服务器移除事件客户端    |

### 事件服务使用

实际开发中,可以将任务拆分后,分成一个一个事件,最终将单一任务事件合并到一个事件服务器中,交由事件服务器分发。

- 裸机编程:将事件服务器放在主函数中运行,可任务异步执行。
- RTOS:将不同任务的事件服务器放在不同线程中运行,可有效优化代码结构,减少线程数量,加速裸机代码移植。

#### 事件服务使用示例:

```c
/* 定义事件 */
#define EVENT1                          1
#define EVENT2                          2
#define EVENT3                          3

/* 定义事件服务器 */
struct mr_event_server event_server;

mr_err_t event1_cb(mr_event_server_t server, void *args)
{
    printf("event1_cb\r\n");

    /* 通知事件服务器事件2发生 */
    mr_event_server_notify(server, EVENT2);

    return MR_ERR_OK;
}

mr_err_t event2_cb(mr_event_server_t server, void *args)
{
    printf("event2_cb\r\n");

    /* 通知事件服务器事件3发生 */
    mr_event_server_notify(server, EVENT3);

    return MR_ERR_OK;
}

mr_err_t event3_cb(mr_event_server_t server, void *args)
{
    printf("event3_cb\r\n");

    return MR_ERR_OK;
}

int main(void)
{
        /* 添加事件服务器到内核容器 */
    mr_event_server_add(&event_server, "server", 4);

        /* 创建事件客户端到事件服务器 */
    mr_event_client_create(EVENT1, event1_cb, MR_NULL, &event_server);
    mr_event_client_create(EVENT2, event2_cb, MR_NULL, &event_server);
    mr_event_client_create(EVENT3, event3_cb, MR_NULL, &event_server);

    /* 通知事件服务器事件1发生 */
    mr_event_server_notify(&event_server, EVENT1);

    while (1)
    {
        mr_event_server_handle(&event_server);
    }
}
```
现象:
```c
event1_cb
event2_cb
event3_cb
```

----------

# 设备

硬件抽象成设备,通过统一的设备操作接口进行交互。

## 设备原型

```c
struct mr_device
{
    struct mr_object object;                                        /* 设备对象基类 */

    enum mr_device_type type;                                       /* 设备类型 */
    mr_uint16_t support_flag;                                       /* 设备支持的打开方式 */
    mr_uint16_t open_flag;                                          /* 设备状态 */
    mr_size_t ref_count;                                            /* 设备被引用次数 */
    void *data;                                                     /* 设备数据 */

    mr_err_t (*rx_cb)(mr_device_t device, void *args);              /* 设备接收回调函数 */
    mr_err_t (*tx_cb)(mr_device_t device, void *args);              /* 设备发送回调函数 */

    const struct mr_device_ops *ops;                                /* 设备操作方法 */
};
```

- 设备支持的打开方式:设备只能以支持的打开方式打开。
- 设备被引用次数:设备每被打开一次,引用+1,设备引用次数为0时设备关闭。
- 设备数据:设备运行所需的数据。

### 设备类型

```c
enum mr_device_type
{
    MR_DEVICE_TYPE_NONE,                                            /* 无类型设备 */
    MR_DEVICE_TYPE_PIN,                                             /* GPIO设备 */
    MR_DEVICE_TYPE_SPI_BUS,                                         /* SPI总线设备 */
    MR_DEVICE_TYPE_SPI,                                             /* SPI设备 */
    MR_DEVICE_TYPE_I2C_BUS,                                         /* I2C总线设备 */
    MR_DEVICE_TYPE_I2C,                                             /* I2C设备 */
    MR_DEVICE_TYPE_SERIAL,                                          /* UART设备*/
    MR_DEVICE_TYPE_ADC,                                             /* ADC设备 */
    MR_DEVICE_TYPE_DAC,                                             /* DAC设备 */
    MR_DEVICE_TYPE_PWM,                                             /* PWM设备 */
    MR_DEVICE_TYPE_TIMER,                                           /* TIMER设备 */
    MR_DEVICE_TYPE_FLASH,                                           /* FLASH设备 */
    /* ... */
};
```

### 设备操作方法

设备通过设备操作接口,最终会调用设备数据块中的设备操作方法。设备仅需实现设备打开方式所必须的方法即可。

```c
struct mr_device_ops
{
    mr_err_t (*open)(mr_device_t device);
    mr_err_t (*close)(mr_device_t device);
    mr_err_t (*ioctl)(mr_device_t device, int cmd, void *args);
    mr_ssize_t (*read)(mr_device_t device, mr_off_t pos, void *buffer, mr_size_t size);
    mr_ssize_t (*write)(mr_device_t device, mr_off_t pos, const void *buffer, mr_size_t size);
};
```

| 方法    | 描述                                                       |
|:------|:---------------------------------------------------------|
| open  | 打开设备,同时完成设备配置。仅当设备为首次被打开时,会调用此方法打开设备。                    |
| close | 关闭设备。仅当设备被所有用户关闭时(设备引用次数为0),会调用此方法关闭设备。                  |
| ioctl | 控制设备。根据cmd命令控制设备。                                        |
| read  | 从设备读取数据,pos是设备读取位置(不同设备所表示意义不同,请查看设备详细手册),size为设备读取字节大小。 |
| write | 向设备写入数据,pos是设备写入位置(不同设备所表示意义不同,请查看设备详细手册),size为设备写入字节大小。 |

### 设备操作接口

| 接口              | 描述        |
|:----------------|:----------|
| mr_device_add   | 添加设备到内核容器 |
| mr_device_find  | 从内核容器查找设备 |
| mr_device_open  | 打开设备      |
| mr_device_close | 关闭设备      |
| mr_device_ioctl | 控制设备      |
| mr_device_read  | 从设备读取数据   |
| mr_device_write | 向设备写入数据   |

#### GPIO设备使用示例:
```c
/* 寻找PIN设备 */
mr_device_t pin_device = mr_device_find("pin");

/* 以可读可写的方式打开PIN设备 */
mr_device_open(pin_device, MR_OPEN_RDWR);

/* 配置B13引脚为推挽输出模式 */
struct mr_pin_config pin_config = { 29, MR_PIN_MODE_OUTPUT };
mr_device_ioctl(pin_device, MR_CTRL_CONFIG, &pin_config);

/* 设置B13为高电平 */
mr_uint8_t pin_level = 1;
mr_device_write(pin_device, 29, &pin_level, sizeof(pin_level));

/* 获取B13电平 */
mr_device_read(pin_device, 29, &pin_level, sizeof(pin_level));

/* 定义回调函数 */
mr_err_t pin_device_cb(mr_device_t device, void *args)
{
    mr_uint32_t *line = args;           /* 获取中断源 */

    /* 判断中断源是line-13 */
    if (*line == 13)
    {
            /* Do something */
    }
}

/* 绑定PIN函数回调函数 */
mr_device_ioctl(pin_device, MR_CTRL_SET_RX_CB, pin_device_cb);
```

----------

# [仓库链接](https://gitee.com/MacRsh/mr-library.git)(https://gitee.com/MacRsh/mr-library.git

----------

# 许可协议

遵循 **Apache License 2.0** 开源许可协议,可免费应用于商业产品,无需公开私有代码。

----------

# 贡献代码



使用特权

评论回复
沙发
caigang13| | 2023-6-7 19:14 | 只看该作者
现在嵌入式也玩容器的概念啊

使用特权

评论回复
板凳
MacRsh|  楼主 | 2023-6-8 02:05 | 只看该作者
caigang13 发表于 2023-6-7 19:14
现在嵌入式也玩容器的概念啊

容器(把东西都放进去),和你想的那个沙盒技术不一样撞名了

使用特权

评论回复
地板
qcliu| | 2023-7-3 13:41 | 只看该作者
这个嵌入式的软件库是哪个厂家开发的啊

使用特权

评论回复
评论
MacRsh 2023-7-5 20:53 回复TA
自己写的 
5
drer| | 2023-7-3 14:12 | 只看该作者
请问这个嵌入式软件库是沁恒自己开发的?

使用特权

评论回复
6
coshi| | 2023-7-3 15:34 | 只看该作者
它的优势以及主要的用途是什么呢?可以免费使用吗

使用特权

评论回复
评论
MacRsh 2023-7-5 20:56 回复TA
主要优势是不同平台移植快,访问接口统一。开源代码,只需要保留协议信息即可,免费使用。 
7
kxsi| | 2023-7-3 15:50 | 只看该作者
这个嵌入式的软件库对我们是完全开源的吗

使用特权

评论回复
评论
MacRsh 2023-7-5 20:56 回复TA
完全开源,代码都在gitee上。 
8
wiba| | 2023-7-3 16:21 | 只看该作者
这个软件库的接口对用户来说友好吗?易读吗?

使用特权

评论回复
评论
MacRsh 2023-7-5 20:58 回复TA
接口完全统一,移植效率高,访问也便携,只需要实现简单的接口配置,就可以实现很多功能。代码写法比较规范,相对是比较易读的。 
9
tpgf| | 2023-7-3 16:41 | 只看该作者
这个软件库和官方的库文件的兼容性好不好啊?

使用特权

评论回复
评论
MacRsh 2023-7-5 21:00 回复TA
软件库与官方库文件完全解耦,库是高于厂商库文件的。驱动部分只需要实现几个接口就可以使用,事件驱动框架等是无依赖的,直接就可以使用。 
10
chenqianqian| | 2023-7-3 20:52 | 只看该作者
这个是个人开发的还是一个团队开发的,有人维护吗?

使用特权

评论回复
评论
MacRsh 2023-7-5 21:01 回复TA
还是个人在开发的状态,我在使用和维护的。 
11
Tompannj| | 2023-8-3 13:16 | 只看该作者
牛X,有点模仿RT-Thread,不过很牛xx

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

5

主题

16

帖子

1

粉丝