いわゆるイベントは、単にメッセージとして理解することができます。
通常、プログラムを作成する人はメッセージキューを使用しますが、MCUは実際にはコアであり、通常は循環プログラムであるため、これは非同期処理です。
主周波数は高速で、ポーリング処理速度は高速であり、プログラム設計要件のほとんどを満たすことができます。現在、多くのステートマシンまたはシングルチップマイクロコンピュータがこれを行っています。
したがって、メッセージキューは非常に重要な機能です。
QPnでは、従来の使用法と同様です。メッセージを直接送信するのはバッファへのデータです。たとえば、プレスメッセージは0x0aであり、外部トリガーハードウェアプレスはマイクロコントローラーによってチェックおよび処理されて、メッセージ0x0Aをに送信します。バッファ。
QPcでは少し改善されています。実際、彼が送信するのはデータですが、このデータはメッセージのアドレスです。考えてみれば、多くの可能性があります。
実際、QPnを変更すれば達成することもできますが、個人的には、共通の理解に基づいてコードを誰もが理解できるようにするためだと思います。
このステートマシンのもう1つの特徴は、各ステートマシンに独自のバッファがあることです。これは少し興味深いです。実際、多くのシングルチップマイクロコンピュータプログラムには、リングバッファが1つしかありません。
彼が実際には単なるステートマシンであることも理解できます。
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
}
/*启动状态机,启动定时器中断,查找优先级最高的状态机,然后处理它的消息*/