所谓的事件 可以简单的理解就是消息。
一般写程序的都会用上消息队列,这个就是异步处理,因为MCU实际就一个核心,一般都是循环的程序。
只要主频快,轮询处理的速度快,就可以满足大部分的程序设计要求,目前看了很多的状态机源码,单片机基本都是这么干的。
所以消息队列是很重要的一个功能。
在QPnano中和传统的用法差不多,直接发送消息也就是数据到 缓冲中,比如按下 消息为0x0a,外部触发硬件按下被单片机检查处理后发送消息0x0A到缓冲中。
在QPc中就稍微提升了一点,他其实发送的也是数据,但是这个数据是 消息的地址,仔细想想这样就又很多可能。
这个状态机还有一个特点,是每个状态机都有自己的缓冲区,这个就有点意思了,实际上很多单片机程序都是只有一个环形缓冲。
typedef struct {
QSignal sig; /*!< signal of the event instance */
uint8_t poolId_; /*!< pool ID (0 for static event) */
uint8_t volatile refCtr_; /*!< reference counter */
} QEvt;
void QF_poolInit(void * const poolSto, uint_fast32_t const poolSize,
uint_fast16_t const evtSize)
{
/** @pre cannot exceed the number of available memory pools */
Q_REQUIRE_ID(200, QF_maxPool_ < Q_DIM(QF_pool_));
/** @pre please initialize event pools in ascending order of evtSize: */
Q_REQUIRE_ID(201, (QF_maxPool_ == 0U)
|| (QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - 1U])
< evtSize));
/* perform the platform-dependent initialization of the pool */
QF_EPOOL_INIT_(QF_pool_[QF_maxPool_], poolSto, poolSize, evtSize);
++QF_maxPool_; /* one more pool */
#ifdef Q_SPY
/* generate the object-dictionary entry for the initialized pool */
{
char_t obj_name[9] = "EvtPool?";
obj_name[7] = '0' + (QF_maxPool_ & 0x7FU);
QS_obj_dict_pre_(&QF_pool_[QF_maxPool_ - 1U], obj_name);
}
#endif /* Q_SPY*/
}
/*
事件的内存池
按照事件的大小设计。
本人一般不用这些模块,尽量简化简单设计。
*/
创建状态机
void QActive_start_(QActive * const me, uint_fast8_t prio,
QEvt const * * const qSto, uint_fast16_t const qLen,
void * const stkSto, uint_fast16_t const stkSize,
void const * const par)
{
(void)stkSize; /* unused parameter */
/** @pre The priority must be in range and the stack storage must not
* be provided, because the QV kernel does not need per-AO stacks.
*/
Q_REQUIRE_ID(500, (0U < prio) && (prio <= QF_MAX_ACTIVE)
&& (stkSto == (void *)0));
QEQueue_init(&me->eQueue, qSto, qLen); /* initialize the built-in queue */
me->prio = (uint8_t)prio; /* set the current priority of the AO */
QF_add_(me); /* make QF aware of this active object */
QHSM_INIT(&me->super, par, me->prio); /* top-most initial tran. */
QS_FLUSH(); /* flush the trace buffer to the host */
}
/*
初始化自己的消息队列
添加状态机
初始化状态机入口函数
*/
int_t QF_run(void) {
#ifdef Q_SPY
uint_fast8_t pprev = 0U; /* previously used priority */
#endif
QF_onStartup(); /* application-specific startup callback */
/* the combined event-loop and background-loop of the QV kernel... */
QF_INT_DISABLE();
/* produce the QS_QF_RUN trace record */
QS_BEGIN_NOCRIT_PRE_(QS_QF_RUN, 0U)
QS_END_NOCRIT_PRE_()
for (;;) {
QEvt const *e;
QActive *a;
uint_fast8_t p;
/* find the maximum priority AO ready to run */
if (QPSet_notEmpty(&QV_readySet_)) {
QPSet_findMax(&QV_readySet_, p);
a = QF_active_[p];
#ifdef Q_SPY
QS_BEGIN_NOCRIT_PRE_(QS_SCHED_NEXT, a->prio)
QS_TIME_PRE_(); /* timestamp */
QS_2U8_PRE_(p, /* priority of the scheduled AO */
pprev); /* previous priority */
QS_END_NOCRIT_PRE_()
pprev = p; /* update previous priority */
#endif /* Q_SPY */
QF_INT_ENABLE();
/* perform the run-to-completion (RTC) step...
* 1. retrieve the event from the AO's event queue, which by this
* time must be non-empty and The "Vanialla" kernel asserts it.
* 2. dispatch the event to the AO's state machine.
* 3. determine if event is garbage and collect it if so
*/
e = QActive_get_(a);
QHSM_DISPATCH(&a->super, e, a->prio);
QF_gc(e);
QF_INT_DISABLE();
if (a->eQueue.frontEvt == (QEvt *)0) { /* empty queue? */
QPSet_remove(&QV_readySet_, p);
}
}
else { /* no AO ready to run --> idle */
#ifdef Q_SPY
if (pprev != 0U) {
QS_BEGIN_NOCRIT_PRE_(QS_SCHED_IDLE, 0U)
QS_TIME_PRE_(); /* timestamp */
QS_U8_PRE_(pprev); /* previous priority */
QS_END_NOCRIT_PRE_()
pprev = 0U; /* update previous priority */
}
#endif /* Q_SPY */
/* QV_onIdle() must be called with interrupts DISABLED because
* the determination of the idle condition (no events in the
* queues) can change at any time by an interrupt posting events
* to a queue. QV_onIdle() MUST enable interrupts internally,
* perhaps at the same time as putting the CPU into a power-saving
* mode.
*/
QV_onIdle();
QF_INT_DISABLE();
}
}
#ifdef __GNUC__ /* GNU compiler? */
return 0;
#endif
}
/*启动状态机,启动定时器中断,查找优先级最高的状态机,然后处理它的消息*/