乐趣区

关于嵌入式:mrlibrary-开源嵌入式驱动框架

背景

随着国产微控制器的崛起,市场上的微控制器品种越来越多。然而,以前的微控制器开发往往疏忽了整体框架和程序分层,导致更换微控制器型号往往须要更改应用层代码,这使得开发工作变得沉重且乏味。常见的开发方式大多分为两种:
常见的开发方式通常分为两种:裸机编程和 RTOS 编程,因为两种形式的代码编写形式存在微小差别,因而在两种形式之间切换意味着须要进行大规模的工程批改。
mr-library 的指标是帮忙开发者进步开发效率和代码通用性,升高平台迁徙的难度。

———-

mr-library 简介

mr-library 是一个嵌入式软件库,齐全采纳 C 语言编写,应用面向对象的设计办法,代码框架清晰,能够疾速移植到不同的平台。它包含以下局部:

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

    ———-

代码目录

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

名称 形容
bsp 板级反对包
device 设施文件
document 文档
driver 驱动文件
include 库头文件
module 组件
package 软件包
src 库源文件

———-

内核

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

容器

容器负责对立治理注册到内核中的对象。

容器原型

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

    enum mr_container_type type;                                    /* 容器类型 */
};
  • 容器链表: 所有注册到容器的对象都将链接到容器链表上,当对象被移除容器时也将从容器链表上移除。
  • 容器类型: 指定容器类型用以寄存指定类型对象。

容器类型

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

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

对象

对象原型

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 重命名对象

服务

事件服务

事件服务器是一种异步事件处理机制, 它通过事件散发和回调的形式, 能够无效地进步零碎的异步解决能力、解耦性和可扩展性。

事件服务器蕴含两个次要组件: 事件服务器和事件客户端。

  • 事件服务器用于接管和散发事件, 它外部保护一个事件队列用于存储待处理事件和一个事件列表用于存储注册的事件客户端。
  • 事件客户端用于解决特定类型的事件, 它须要注册到事件服务器并提供一个回调函数。

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

事件服务原型

/* 事件服务器 */
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: 将不同工作的事件服务器放在不同线程中运行,可无效优化代码构造,缩小线程数量,减速裸机代码移植。
事件服务应用示例:
/* 定义事件 */
#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);
    }
}

景象:

event1_cb
event2_cb
event3_cb

———-

设施

硬件形象成设施,通过对立的设施操作接口进行交互。

设施原型

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 时设施敞开。
  • 设施数据: 设施运行所需的数据。

设施类型

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 设施 */
    /* ... */
};

设施操作方法

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

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 设施应用示例:
/* 寻找 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)

———-

许可协定

遵循 Apache License 2.0 开源许可协定,可收费利用于商业产品,无需公开公有代码。

———-

奉献代码

如果您对 mr-library 我的项目感兴趣,欢送参加开发并成为代码贡献者。欢送退出探讨群 199915649(QQ) 分享您的观点。

退出移动版