本期推荐的EventOS Nano,是一个面向单片机、事件驱动的嵌入式开发平台。
EventOS Nano介绍
它主要有两大技术特色:一是事件驱动,二是超轻量。EventOS Nano以及其母项目EventOS,目标是开发一个企业级的嵌入式开发平台,以事件总线为核心,打造一个统一的嵌入式技术生态,为广大企业用户和嵌入式开发者们,提供搞可靠性的、高性能的、现代且高开发效率的嵌入式开发环境。
EventOS Nano的主要特性列举如下:
- 事件总线为核心组件,灵活易用,是进行线程(状态机)间同步或者通信的主要手段,也是对EventOS分布式特性和跨平台开发进行支持的唯一手段。事件支持广播发送,或者发布-订阅机制两种方式(二选一)。
- 全局事件队列,摒弃每个线程(状态机)拥有自己的事件队列的方式,只有一个全局事件队列,极限压缩对RAM的占用。
- 协作式内核,优点是不会产生资源竞争,极度可靠。
- 极度轻量,便于嵌入其他系统,除事件总线外的所有特性(层次状态机、平面状态机、发布-订阅机制、事件携带数据、事件桥等)均可裁剪,将资源占用降至极限,可低至ROM 1.2KB,RAM 172Byte。可以作为子系统,“悄悄”嵌入到其他软件系统中去。
- 功能强大的软定时器,以时间事件的形式,对软定时器功能,进行优雅且功能强大的实现。
- API的设计,更加简明,更加符合本土嵌入式工程师的习惯。
- 移植方便,只需实现少数几个接口函数即可。
- 未来会使用Event Bridge(事件桥)机制与EventOS打通事件总线,以便对EventOS的分布式特性进行支持。
- 重点关注三种应用场景:单片机,作为模块向其他软件系统的嵌入和可靠性要求较高的嵌入式场景。
EventOS Nano主张的编程思想
事件驱动与事件总线
事件驱动和事件总线,是EventOS Nano的核心,也是EventOS的核心。事件机制,与RTOS中事件概念完全不同,它更像是windows编程中的消息。事件,可以认为是主题 + 不定长数据,通过事件,可以极大解耦模块间的耦合,增强软件的可测试性,还可以进行跨平台开发和分布式扩展。
防御式编程
EventOS Nano使用了大量的断言,对系统的运行过程和用户对EventOS Nano的使用进行大量的检查。我们强烈建议,用户要对断言接口函数进行精心的设计和实现,在实际的产品代码中,依然打开断言。这样,软件将以非常快的速度,收敛于稳定状态。
跨平台开发
EventOS Nano提倡跨平台开发。所谓跨平台开发,就是在Windows和Linux等便捷友好的开发环境里,完成绝大部分的开发工作,包括编程、调试、运行和单元测试等工作,然后在目标平台上进行最后的移植、调试和适配工作。跨平台的优点有很多,比如开发效率非常高、工程师进入到更多的编程领域和程序稳定可靠等。EventOS Nano主要在32位MinGW平台和Linux平台上开发。开发环境的搭建,见文档开发环境搭建。当然,也完全可以用MDK在单片机上直接开发,效率稍低而已。
消除耦合
无论是广播式的事件发送机制,还是发布-订阅式的事件发送机制,实际上,都是为了消除软件模块间的耦合。
快速入门
EventOS Nano的入门非常简单。除源码外,只需要实现三个代码,就可以使用EventOS Nano来编写程序。
- main.c main函数,初始化和启动EventOS Nano。
- eos_port.c 如EventOS在特定平台上的接口实现,也就是EventOS Nano移植的相关代码。
- eos_led.c LED的闪烁状态机。LED灯闪烁,就是单片机界的Hello World。相信是很多人的入门代码。
main.c 从EventOS启动的过程非常简单,短短几个步骤就能启动。
/* include ------------------------------------------------------------------ */
#include "eventos.h" // EventOS Nano头文件
#include "event_def.h" // 事件主题的枚举
#include "eos_led.h" // LED灯闪烁状态机
/* define ------------------------------------------------------------------- */
static eos_u32_t eos_sub_table[Event_Max]; // 订阅表数据空间
static eos_u8_t eos_heap_memory[1024]; // 事件池空间
/* main function ------------------------------------------------------------ */
int main(void)
{
// EventOS Nano的初始化
eos_init(); // EventOS初始化
eos_sub_init(eos_sub_table); // 订阅表初始化
eos_event_pool_init(eos_heap_memory, 1024); // 事件池初始化
// 状态机模块的初始化
eos_led_init(); // LED状态机初始化
// 启动EventOS Nano。
eos_run(); // EventOS启动并运行
return 0;
}
事件主题event_def.h的定义如下。
#include "eventos.h"
enum {
Event_Test = Event_User, // 事件主题的定义从Event_User开始,小于Event_User的是系统事件。
Event_Time_500ms,
Event_Max
};
eos_port.c 移植文件,在《UM-02-002 EventOS Nano移植文档》中已经详细说明,不再赘述。
eos_led.c和eos_led.h 头文件不说了,重点说.c文件,也就是状态机是如何使用的。
/* include ------------------------------------------------------------------ */
#include "eos_led.h" // 模块头文件
#include "eventos.h" // EventOS头文件
#include "event_def.h" // 事件定义头文件
#include <stdio.h> // 标准输入输出库
/* data structure ----------------------------------------------------------- */
typedef struct eos_led_tag { // LED类
eos_sm_t super;
eos_bool_t status;
} eos_led_t;
static eos_led_t led; // led对象,单例模式
/* static state function ---------------------------------------------------- */
// 初始状态
static eos_ret_t state_init(eos_led_t * const me, eos_event_t const * const e);
// Led的ON状态
static eos_ret_t state_on(eos_led_t * const me, eos_event_t const * const e);
// Led的Off状态
static eos_ret_t state_off(eos_led_t * const me, eos_event_t const * const e);
/* api ---------------------------------------------------- */
void eos_led_init(void)
{
static eos_u32_t queue[32]; // 事件队列
eos_sm_init(&led.super, 1, queue, 32); // 状态机初始化
// 状态机启动,以state_init作为初始状态。
eos_sm_start(&led.super, EOS_STATE_CAST(state_init));
led.status = 0;
}
/* static state function ---------------------------------------------------- */
static eos_ret_t state_init(eos_led_t * const me, eos_event_t const * const e)
{
// 订阅事件Event_Time_500ms
EOS_EVENT_SUB(Event_Time_500ms);
// 使事件Event_Time_500ms,每隔500ms就被发送一次。
eos_event_pub_period(Event_Time_500ms, 500);
return EOS_TRAN(state_off);
}
static eos_ret_t state_on(eos_led_t * const me, eos_event_t const * const e)
{
switch (e->topic) {
case Event_Enter: // 状态state_on的进入事件
printf("State On!\n");
me->status = 1;
return EOS_Ret_Handled;
case Event_Time_500ms: // 收到Event_Time_500ms,跳转到state_off
return EOS_TRAN(state_off);
default:
return EOS_SUPER(eos_state_top);
}
}
static eos_ret_t state_off(eos_led_t * const me, eos_event_t const * const e)
{
switch (e->topic) {
case Event_Enter: // 状态state_on的进入事件
printf("State Off!\n");
me->status = 0;
return EOS_Ret_Handled;
case Event_Time_500ms: // 收到Event_Time_500ms,跳转到state_on
return EOS_TRAN(state_on);
default:
return EOS_SUPER(eos_state_top);
}
}
代码结构
核心代码
- eventos/eventos.c EventOS Nano状态机框架的实现
- eventos/eventos.h 头文件
- eventos/eventos_config.h 对EventOS Nano进行配置与裁剪
第三方代码库
- RTT Segger JLink所提供的日志库,依赖于JLink硬件。
- unity 单元测试框架