嵌入式程序普通事件调度

嵌入式程序普通事件调度

  • 背景

    在嵌入式开发过程中,通常使用中断来驱动各种事件,但是有时需要执行一些较为耗时的操作,中断时是不建议执行一些耗时操作的,比如在中断中打印字符串,在串口波特率为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循环里的话,就需要在事件中手动喂狗,如果在计时器中断中喂狗,那就无所谓了,但是还是会耽误其他任务的执行,这个时候建议使用周期性事件,在周期性事件中,判断事件的进展,事件结束,也会从相应的链表中删除。
  • 通过这样的事件调度机制,可以使得我们在中断从容的下方一些耗时操作,甚至是耗时很久的操作(通过周期性事件),但是如果对事件精度要求很高,那么还是得需要通过定时器或者其他中断来执行相应的事件。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值