【EventOS Nano】EventOS Nano框架分析
1.核心数据结构
时间触发型事件结构体
// eos定时器事件
typedef struct eos_event_timer {
eos_u32_t topic : 13; // 事件主题
eos_u32_t oneshoot : 1; // 是否单次触发
eos_u32_t unit : 2; // 时间单位,共支持4种时间单位
eos_u32_t period : 16; // 触发周期时间值
eos_u32_t timeout_ms; // 时间触发型事件超时时间(当前系统时间+触发周期时间)
} eos_event_timer_t;
// 可以理解为角色?
typedef struct eos_actor {
eos_u32_t priority : 5; // 优先级
eos_u32_t mode : 1; // 模式
eos_u32_t enabled : 1; // 使能位
eos_u32_t reserve : 1;
} eos_actor_t;
为了将占用内存控制到最小,将eos_tag定义为全局变量
typedef struct eos_tag
{
eos_u32_t magic;
eos_mcu_t *sub_table; // 事件订阅表
// eos的actor参量
eos_mcu_t actor_exist; // actor存在标志位
eos_mcu_t actor_enabled; // acotr使能标志位
eos_actor_t *actor[EOS_MAX_ACTORS]; // actor表
// eos 的堆空间变量
eos_heap_t heap;
// eos时间触发型事件
eos_event_timer_t etimer[EOS_MAX_TIME_EVENT]; // 时间触发型事件表
eos_u32_t time; // 系统时基(时间)
eos_u32_t timeout_min; // 超时事件最短的定时器
eos_u8_t timer_count; // 定时器事件数量
// eos框架运行标志
eos_u8_t enabled :1; // 是否使能
eos_u8_t running :1; // 运行状态
eos_u8_t init_end :1;
}eos_t;
// 事件全局变量
static eos_t eos;
2.系统初始化
在Stm32F3的MCU平台上,eos的时基,是由Systick定时器中断提供的(stm32f10x_it.c),在刚开始移植到到Linux平台下做仿真调试的时候,发现eos_run()后,eos上并没有运行,发现是因为移植到Linux环境后就没有Systick硬定时器,所以创建了一个软件定时器,在软件定时器中执行eos_tick()。
// timer-----------------------------------------
//定时器触发函数
void SysTickHandler(int signo){
eos_tick(); // 时钟中断一次,执行一次,eos.time+1
}
void init_sigaction(){
struct sigaction act;
act.sa_handler = SysTickHandler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGPROF,&act,NULL); //设置信号 SIGPROF 的处理函数为 SysTickHandler
}
void init_time() {
struct itimerval value;
value.it_value.tv_sec=1; //定时器启动后,每隔1秒将执行相应的函数
value.it_value.tv_usec=0;
value.it_interval=value.it_value;
setitimer(ITIMER_PROF,&value,NULL); //初始化 timer,到期发送 SIGPROF 信号
}
* main function ------------------------------------------------------------ */
int main(void)
{
init_sigaction();
init_time();
eos_init(); // EventOS初始化
#if (EOS_USE_PUB_SUB != 0)
eos_sub_init(eos_sub_table, Event_Max); // 订阅表初始化
#endif
#if (EOS_USE_SM_MODE != 0)
eos_sm_led_init(); // LED状态机初始化
#endif
eos_reactor_led_init();
eos_run(); // EventOS启动
return 0;
}
eos框架初始化
void eos_init(void)
{
eos_clear(); // 将timer_count清为0
eos.enabled = EOS_True; // eos框架使能
eos.running = EOS_False; // eos没有正在运行
eos.actor_exist = 0; // 存在的状态机
eos.actor_enabled = 0; // 状态机不使能
eos.sub_table = EOS_NULL; // 订阅表为空
eos_heap_init(&eos.heap);
eos.init_end = 1;
eos.time = 0; // 时基初始化
}
// 初始化发布-订阅表
void eos_sub_init(eos_mcu_t *flag_sub, eos_topic_t topic_max)
{
eos.sub_table = flag_sub;
for (int i = 0; i < topic_max; i ++) {
eos.sub_table[i] = 0;
}
}
3.事件发布
eos中有两种事件类型,分别为时间触发型事件和携带数据型事件,时间触发型事件又分为周期事件和非周期事件。
3.1 时间触发型事件发布
发布周期触发型事件
void eos_event_pub_period(eos_topic_t topic, eos_u32_t time_ms_period)
{
// topic:事件主题 time_ms_period:触发周期 EOS_False:是否只触发一次
eos_event_pub_time(topic, time_ms_period, EOS_False);
}
发布延时时间事件(非周期时间型事件)
void eos_event_pub_delay(eos_topic_t topic, eos_u32_t time_ms)
{
// topic:事件主题 time_ms:延时触发时间 EOS_True:只触发一次
eos_event_pub_time(topic, time_ms, EOS_True);
}
两种时间型事件都是调用的eos_event_pub_time函数,原理是计算发布的时间型事件etimer(eos_event_timer_t类型)各项成员值,赋给eos.etimer[i],同时时间型事件数自增(eos.timer_count++):
void eos_event_pub_time(eos_topic_t topic, eos_u32_t time_ms, eos_bool_t oneshoot)
{
EOS_ASSERT(time_ms != 0);
EOS_ASSERT(time_ms <= timer_threshold[EosTimerUnit_Minute]);
EOS_ASSERT(eos.timer_count < EOS_MAX_TIME_EVENT);
// 检查重复,不允许重复发送。
for (eos_u32_t i = 0; i < eos.timer_count; i ++) {
EOS_ASSERT(topic != eos.etimer[i].topic);
}
eos_u32_t system_ms = eos.time;
eos_u8_t unit = EosTimerUnit_Ms;
eos_u16_t period;
for (eos_u8_t i = 0; i < EosTimerUnit_Max; i ++) {
if (time_ms > timer_threshold[i])
continue;
unit = i;
if (i == EosTimerUnit_Ms) {
period = time_ms;
break;
}
period = (time_ms + (timer_unit[i] >> 1)) / timer_unit[i];
break;
}
eos_u32_t timeout = (system_ms + time_ms);
// 将计算好的值,赋给eos.etimer[eos.timer_count ++]
eos.etimer[eos.timer_count ++] = (eos_event_timer_t) {
topic, oneshoot, unit, period, timeout
};
// 更新系统当前最小的超时时间值
if (eos.timeout_min > timeout) {
eos.timeout_min = timeout;
}
}
如果想取消时间型事件的发布可以调用:
void eos_event_time_cancel(eos_topic_t topic);
实现原理就是遍历eos.etimer[i],将传入topic对应的删除,并同步eos.timer_count -=1,重新计算所有事件计时器中最小的事件赋给eos.timeout_min。
3.2 携带数据型事件发布
只发布事件主题(topic)
void eos_event_pub_topic(eos_topic_t topic)
{
// 入参只有topic
eos_s8_t ret = eos_event_pub_ret(topic, EOS_NULL, 0);
EOS_ASSERT(ret >= 0);
(void)ret;
}
发布携带数据的事件(topic+*data)
void eos_event_pub(eos_topic_t topic, void *data, eos_u32_t size)
{
eos_s8_t ret = eos_event_pub_ret(topic, data, size);
EOS_ASSERT(ret >= 0);
(void)ret;
}
两种方法调用的都是eos_event_pub_ret:
eos_s8_t eos_event_pub_ret(eos_topic_t topic, void *data, eos_u32_t size)
{
if (eos.init_end == 0) {
return (eos_s8_t)EosRunErr_NotInitEnd;
}
#if (EOS_USE_PUB_SUB != 0)
if (eos.sub_table == EOS_NULL) {
return (eos_s8_t)EosRunErr_SubTableNull;
}
#endif
// 保证框架已经运行
if (eos.enabled == 0) {
return (eos_s8_t)EosRun_NotEnabled;
}
if (eos.actor_exist == 0) {
return (eos_s8_t)EosRun_NoActor;
}
// 没有状态机使能,返回
if (eos.actor_enabled == 0) {
return (eos_s8_t)EosRun_NotEnabled;
}
// 没有输入topic事件的状态机订阅,返回
#if (EOS_USE_PUB_SUB != 0)
if (eos.sub_table[topic] == 0) {
return (eos_s8_t)EosRun_NoActorSub;
}
#endif
eos_port_critical_enter();
// 申请事件空间
eos_event_inner_t *e = eos_heap_malloc(&eos.heap, (size + sizeof(eos_event_inner_t)));
if (e == (eos_event_inner_t *)0) {
eos_port_critical_exit();
return (eos_s8_t)EosRunErr_MallocFail;
}
e->topic = topic;
#if (EOS_USE_PUB_SUB != 0)
e->sub = eos.sub_table[e->topic];
#else
e->sub = eos.actor_exist;
#endif
eos.heap.sub_general |= e->sub;
eos_u8_t *e_data = (eos_u8_t *)e + sizeof(eos_event_inner_t);
for (eos_u32_t i = 0; i < size; i ++) {
e_data[i] = ((eos_u8_t *)data)[i];
}
eos_port_critical_exit();
return (eos_s8_t)EosRun_OK;
}
3.3 eos的运行和事件的处理
eos_run循环调用eos_once()
void eos_run(void)
{
eos_hook_start();
EOS_ASSERT(eos.enabled == EOS_True);
#if (EOS_USE_PUB_SUB != 0)
EOS_ASSERT(eos.sub_table != 0);
#endif
#if (EOS_USE_EVENT_DATA != 0 && EOS_USE_HEAP != 0)
EOS_ASSERT(eos.heap.size != 0);
#endif
eos.running = EOS_True;
// eos一直处于使能状态
while (eos.enabled) {
// 实际一直调用eos_once()
eos_s8_t ret = eos_once();
EOS_ASSERT(ret >= 0);
if (ret == EosRun_NotEnabled) {
break;
}
if (ret == EosRun_NoActor || ret == EosRun_NoEvent) {
#if (EOS_USE_MAGIC != 0)
EOS_ASSERT(eos.heap.magic == EOS_MAGIC_NUMBER);
EOS_ASSERT(eos.magic == EOS_MAGIC_NUMBER);
for (eos_u8_t i = 0; i < EOS_MAX_ACTORS; i ++) {
if ((eos.actor_exist & (1 << i)) != 0) {
EOS_ASSERT(eos.actor[i]->magic == EOS_MAGIC_NUMBER);
}
}
#endif
eos_hook_idle();
}
}
// 如果EosRun_NotEnabled置位
while (1) {
eos_hook_idle();
}
}
eos_once分别执行SM模式逻辑和reactor模式逻辑
eos_s8_t eos_once(void)
{
if (eos.init_end == 0) {
return (eos_s8_t)EosRunErr_NotInitEnd;
}
#if (EOS_USE_PUB_SUB != 0)
if (eos.sub_table == EOS_NULL) {
return (eos_s8_t)EosRunErr_SubTableNull;
}
#endif
if (eos.enabled == EOS_False) {
eos_clear();
return (eos_s8_t)EosRun_NotEnabled;
}
// 检查是否有状态机的注册
if (eos.actor_exist == 0 || eos.actor_enabled == 0) {
return (eos_s8_t)EosRun_NoActor;
}
#if (EOS_USE_TIME_EVENT != 0)
/* 处理定时器事件,如果有某个定时器时间到达,则发布定时器事件的topic,并寻找
当前定时器数组中最近到达的超时时间,赋给eos.timeout_min*/
eos_evttimer();
#endif
if (eos.heap.empty == EOS_True) {
return (eos_s8_t)EosRun_NoEvent;
}
// 寻找到优先级最高,且有事件需要处理的 Actor
eos_actor_t *actor = eos.actor[0];
eos_u8_t priority = EOS_MAX_ACTORS;
for (eos_s8_t i = (eos_s8_t)(EOS_MAX_ACTORS - 1); i >= 0; i --) {
// 检测是否有事件需要处理
if ((eos.actor_exist & (1 << i)) == 0)
continue;
if ((eos.heap.sub_general & (1 << i)) == 0)
continue;
actor = eos.actor[i];
priority = i;
break;
}
// 如果没有找到,返回
if (priority == EOS_MAX_ACTORS) {
return (eos_s8_t)EosRun_NoActorSub;
}
// 寻找当前Actor的最老的事件
eos_port_critical_enter();
eos_event_inner_t * e = eos_heap_get_block(&eos.heap, priority);
EOS_ASSERT(e != EOS_NULL);
eos_port_critical_exit();
eos_event_t event;
event.topic = e->topic;
event.data = (void *)((eos_pointer_t)e + sizeof(eos_event_inner_t));
eos_block_t *block = (eos_block_t *)((eos_pointer_t)e - sizeof(eos_block_t));
event.size = block->size - block->offset - sizeof(eos_event_inner_t);
// 对事件进行执行
#if (EOS_USE_PUB_SUB != 0)
if ((eos.sub_table[e->topic] & (1 << actor->priority)) != 0)
#endif
{
#if (EOS_USE_SM_MODE != 0)
if (actor->mode == EOS_Mode_StateMachine) {
// SM模式的切换逻辑,执行状态的转换
eos_sm_t *sm = (eos_sm_t *)actor;
eos_sm_dispath(sm, &event);
}
else
#endif
{
// reactor模式的处理逻辑,触发事件
eos_reactor_t *reactor = (eos_reactor_t *)actor;
reactor->event_handler(reactor, &event);
}
}
#if (EOS_USE_PUB_SUB != 0)
else {
return (eos_s8_t)EosRunErr_ActorNotSub;
}
#endif
#if (EOS_USE_EVENT_DATA != 0)
// 销毁过期事件与其携带的参数
eos_port_critical_enter();
eos_heap_gc(&eos.heap, e);
eos_port_critical_exit();
#endif
return (eos_s8_t)EosRun_OK;
}
3.4 eos退出
执行eos_stop,失能eos,并hook_stop执行退出,释放资源
void eos_stop(void)
{
eos.enabled = EOS_False;
eos_hook_stop();
}