嵌入式程序普通事件调度
-
背景
在嵌入式开发过程中,通常使用中断来驱动各种事件,但是有时需要执行一些较为耗时的操作,中断时是不建议执行一些耗时操作的,比如在中断中打印字符串,在串口波特率为115200的情况下,打印一套日志正常需要5ms以上,在一些对时间反应要求情况下,等待几毫秒是不能接受的,特别是一些中断优先级较高的中断中。所以,有没有一个办法,在中断中注册一个行为,然后在main函数的while循环中捕获到这个行为,然后将它执行。
1. 设计框图

- 在上图中,可以看到有两种事件,一种是周期性事件,还有一种是单次事件。
- 周期性事件可以在while循环里循环执行,同时还提供了方法可以获取状态,可以判断事件是否结束,以及本次是否执行,可以执行一些对时间要求不高的周期性时间,如果到时间则执行,未到时间则跳过,如果获取的状态是结束事件,会将事件从链表中删除。
- 单次事件中有个属性是priority,表明此事件的优先级,事件有高优先级和低优先级,高优先级事件,每次轮询都会执行,低优先级事件,只有在高优先级事件不满10个时,和高优先级事件一起可以执行10个,剩下的等待下一轮,单次事件在执行结束后会将事件删除。
- 周期性事件管理器中有一个链表来管理周期性事件,可以执行像喂狗之类的周期性工作,单次事件管理器中,有两个链表,一个高优先级链表,一个低优先级链表,两个管理器都可以去挂载相应的事件,在单次任务挂载时,可能是在中断中进行的,可能会对执行产生影响,所以这里挂载到链表上是需要进入临界区的,周期性任务,并不会进入临界区。
- 周期性事件的删除是依靠管理器轮询看是否结束事件。
2. 代码
- 下面是核心部分的代码,完整的项目工程代码见我的gitee仓库:STM32F103: stm32 personal code repository - Gitee.com
-
MainEvents.h
#ifndef __MAIN_EVENTS_H #define __MAIN_EVENTS_H #ifdef __cplusplus extern "C" { #endif #include <stdbool.h> #include <errno.h> typedef enum { EVENT_OK = 0x00, EVENT_NULL_POINTER, } EventStatus; typedef void (*EventFunc)(void *arg, size_t bufLen); typedef enum { EVENT_HIGH_PRIORITY = 0x00, EVENT_LOW_PRIORITY, } EventPriority; typedef struct SingleEvent_t SingleEvent; struct SingleEvent_t { SingleEvent *next; EventPriority priority; void *arg; size_t bufLen; void (*execute)(void *arg, size_t bufLen); void (*remove)(SingleEvent *pthis); }; SingleEvent *SingleEventCtor(EventPriority priority, EventFunc func, void *arg, size_t bufLen); typedef struct SingleEventsManager_t SingleEventsManager; struct SingleEventsManager_t { SingleEvent *highPriorityHead; SingleEvent *highPriorityTail; SingleEvent *commonHead; SingleEvent *commonTail; size_t highPriorityNum; size_t commonNum; error_t (*mount)(SingleEventsManager *pthis, SingleEvent *event); error_t (*executeEvents)(SingleEventsManager *pthis); void (*remove)(SingleEventsManager *pthis); }; SingleEventsManager *SingleEventsManagerCtor(void); typedef struct { bool isToExe; bool isOver; } PeriodicEventStatus; typedef PeriodicEventStatus (*StatusFunc)(void); typedef struct PeriodicEvent_t PeriodicEvent; struct PeriodicEvent_t { PeriodicEvent *next; PeriodicEvent *prev; uint32_t period; void *arg; size_t bufLen; void (*execute)(void *arg, size_t bufLen); PeriodicEventStatus (*getStatus)(void); void (*remove)(PeriodicEvent *pthis); }; PeriodicEvent *PeriodicEventCtor(EventFunc func, StatusFunc statusFunc, void *arg, size_t bufLen); typedef struct PeriodicEventsManager_t PeriodicEventsManager; struct PeriodicEventsManager_t { PeriodicEvent *periodicEventHead; PeriodicEvent *periodicEventTail; error_t (*scan)(PeriodicEventsManager *pthis); error_t (*mount)(PeriodicEventsManager *pthis, PeriodicEvent *event); void (*remove)(PeriodicEventsManager *pthis); }; PeriodicEventsManager *PeriodicEventsManagerCtor(void); typedef struct EventsManager_t EventsManager; struct EventsManager_t { PeriodicEventsManager *pem; SingleEventsManager *sem; error_t (*execute)(EventsManager *pthis); void (*remove)(EventsManager *pthis); }; EventsManager *EventsManagerCtor(PeriodicEventsManager *pem, SingleEventsManager *sem); void MainEventsInit(void); EventsManager *GetEventManagerHandler(void); #ifdef __cplusplus } #endif #endif -
MainEvents.c
#include <stdlib.h> #include <stdint.h> #include <errno.h> #include "main.h" #include "MainEvents.h" void DeleteSingleEvent(SingleEvent *pthis) { if (pthis != NULL) { if (pthis->bufLen) { free(pthis->arg); } free(pthis); } } SingleEvent *SingleEventCtor(EventPriority priority, EventFunc func, void *arg, size_t bufLen) { SingleEvent *me = (SingleEvent *)malloc(sizeof(SingleEvent)); if (me == NULL) { return NULL; } me->arg = NULL; me->bufLen = bufLen; if (bufLen != 0) { me->arg = malloc(bufLen); } memcpy(me->arg, arg, bufLen); me->next = NULL; me->priority = priority; me->execute =func; me->remove = DeleteSingleEvent; return me; } error_t MountEvent(SingleEventsManager *pthis, SingleEvent *event) { if (pthis == NULL || event == NULL) { return EVENT_NULL_POINTER; } __disable_irq(); if (event->priority == EVENT_HIGH_PRIORITY) { if (pthis->highPriorityHead == NULL) { pthis->highPriorityHead = event; pthis->highPriorityTail = event; } else { pthis->highPriorityTail->next = event; pthis->highPriorityTail = event; } pthis->highPriorityNum++; } else if (event->priority == EVENT_LOW_PRIORITY) { if (pthis->commonHead == NULL) { pthis->commonHead = event; pthis->commonTail = event; } else { pthis->commonTail->next = event; pthis->commonTail = event; } pthis->commonNum++; } __enable_irq(); return EVENT_OK; } #define MAX_SINGLE_EVENTS 10 error_t executeEvents(SingleEventsManager *pthis) { if (pthis == NULL) { return EVENT_NULL_POINTER; } SingleEvent *highEventHead = NULL; SingleEvent *commonEventHead = NULL; SingleEvent *event = NULL; SingleEvent *temp = NULL; size_t highNum = 0; size_t commonNumMax = 0; __disable_irq(); highNum = pthis->highPriorityNum; if (highNum) { highEventHead = pthis->highPriorityHead; pthis->highPriorityHead = NULL; pthis->highPriorityTail = NULL; pthis->highPriorityNum = 0; } if (pthis->commonNum && highNum < MAX_SINGLE_EVENTS) { commonNumMax = MAX_SINGLE_EVENTS - highNum; commonEventHead = pthis->commonHead; if (commonNumMax >= pthis->commonNum) { pthis->commonHead = NULL; pthis->commonTail = NULL; pthis->commonNum = 0; } else { event = pthis->commonHead; for (size_t i = 0; i < commonNumMax - 1; i++) { event = event->next; } pthis->commonHead = event->next; event->next = NULL; pthis->commonNum -= commonNumMax; } } __enable_irq(); // 执行 event = highEventHead; while (event != NULL) { event->execute(event->arg, event->bufLen); temp = event; event = event->next; temp->remove(temp); } event = commonEventHead; while (event != NULL) { event->execute(event->arg, event->bufLen); temp = event; event = event->next; temp->remove(temp); } return EVENT_OK; } void DeleteSingleEventsManager(SingleEventsManager *pthis) { if (pthis != NULL) { free(pthis); } } SingleEventsManager *SingleEventsManagerCtor(void) { SingleEventsManager *me = (SingleEventsManager *)malloc(sizeof(SingleEventsManager)); if (me == NULL) { return NULL; } me->commonHead = NULL; me->commonTail = NULL; me->highPriorityHead = NULL; me->highPriorityTail = NULL; me->commonNum = 0; me->highPriorityNum = 0; me->mount = MountEvent; me->executeEvents = executeEvents; me->remove = DeleteSingleEventsManager; return me; } PeriodicEventStatus PeriodicEventGetStatus(PeriodicEvent *pthis) { static int num = 0; PeriodicEventStatus status = {true, false}; if (num >= 10) { status.isOver = true; } num++; return status; } void DeletePeriodicEvent(PeriodicEvent *pthis) { if (pthis != NULL) { free(pthis); } } PeriodicEvent *PeriodicEventCtor(EventFunc func, StatusFunc statusFunc, void *arg, size_t bufLen) { PeriodicEvent *me = (PeriodicEvent *)malloc(sizeof(PeriodicEvent)); if (me == NULL) { return NULL; } me->next = NULL; me->prev = NULL; me->arg = NULL; me->bufLen = bufLen; if (bufLen != 0) { me->arg = malloc(bufLen); } memcpy(me->arg, arg, bufLen); me->period = 0; me->execute = func; me->getStatus = statusFunc; me->remove = DeletePeriodicEvent; return me; } error_t PeriodicEventsManagerScan(PeriodicEventsManager *pthis) { if (pthis == NULL) { return EVENT_NULL_POINTER; } PeriodicEvent *event = pthis->periodicEventHead; PeriodicEvent *toRemove; PeriodicEventStatus status; while (event != NULL) { status = event->getStatus(); if (status.isToExe) { event->execute(event->arg, event->bufLen); } toRemove = event; event = event->next; if (status.isOver) { if (toRemove->prev == NULL) { pthis->periodicEventHead = toRemove->next; } else { toRemove->prev->next = toRemove->next; } if (toRemove->next == NULL) { pthis->periodicEventTail = toRemove->prev; } else { toRemove->next->prev = toRemove->prev; } toRemove->remove(toRemove); } } return EVENT_OK; } error_t PeriodicEventsManagerMount(PeriodicEventsManager *pthis, PeriodicEvent *event) { if (pthis == NULL || event == NULL) { return EVENT_NULL_POINTER; } if (pthis->periodicEventHead == NULL) { pthis->periodicEventHead = event; pthis->periodicEventTail = event; return EVENT_OK; } pthis->periodicEventTail->next = event; event->prev = pthis->periodicEventTail; pthis->periodicEventTail = event; return EVENT_OK; } void DeletePeriodicEventsManager(PeriodicEventsManager *pthis) { if (pthis != NULL) { free(pthis); } } PeriodicEventsManager *PeriodicEventsManagerCtor(void) { PeriodicEventsManager *me = (PeriodicEventsManager *)malloc(sizeof(PeriodicEventsManager)); if (me == NULL) { return NULL; } me->periodicEventHead = NULL; me->periodicEventTail = NULL; me->mount = PeriodicEventsManagerMount; me->scan = PeriodicEventsManagerScan; me->remove = DeletePeriodicEventsManager; return me; } error_t EventsManagerExecute(EventsManager *pthis) { if (pthis == NULL) { return EVENT_NULL_POINTER; } pthis->pem->scan(pthis->pem); pthis->sem->executeEvents(pthis->sem); return EVENT_OK; } void DeleteEventsManager(EventsManager *pthis) { if (pthis != NULL) { free(pthis); } } EventsManager *EventsManagerCtor(PeriodicEventsManager *pem, SingleEventsManager *sem) { if (pem == NULL || sem == NULL) { return NULL; } EventsManager *me = (EventsManager *)malloc(sizeof(EventsManager)); if (me == NULL) { return NULL; } me->pem = pem; me->sem = sem; me->execute = EventsManagerExecute; me->remove = DeleteEventsManager; return me; } SingleEventsManager *g_seManager = NULL; PeriodicEventsManager *g_peManager = NULL; EventsManager *g_eventManager = NULL; void MainEventsInit(void) { g_seManager = SingleEventsManagerCtor(); g_peManager = PeriodicEventsManagerCtor(); g_eventManager = EventsManagerCtor(g_peManager, g_seManager); } EventsManager *GetEventManagerHandler(void) { return g_eventManager; }
-
3. 总结
- 有时在中断里需要执行一些较为耗时的操作,我们常常是通过设置一个标志位,在main函数的while循环中检测是否需要执行,但是有些操作并不适合这样做,比如想打印一个字符串,或者数量较多的时候,这个时候使用这样的事件注册机制,执行完从链表中删除,是一个较为高效的方式,当然有时也可能会有耗费事件较长的事情,可能需要几秒中,这个时候如果喂狗操作也是在while循环里的话,就需要在事件中手动喂狗,如果在计时器中断中喂狗,那就无所谓了,但是还是会耽误其他任务的执行,这个时候建议使用周期性事件,在周期性事件中,判断事件的进展,事件结束,也会从相应的链表中删除。
- 通过这样的事件调度机制,可以使得我们在中断从容的下方一些耗时操作,甚至是耗时很久的操作(通过周期性事件),但是如果对事件精度要求很高,那么还是得需要通过定时器或者其他中断来执行相应的事件。
2121





