149 lines
4.3 KiB
Plaintext
149 lines
4.3 KiB
Plaintext
/*
|
|
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010 Giovanni Di Sirio.
|
|
|
|
This file is part of ChibiOS/RT.
|
|
|
|
ChibiOS/RT is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
ChibiOS/RT is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* @page article_wakeup How to wake up a thread from an interrupt handler
|
|
* Waking up a thread after an hardware event is one of the most common tasks
|
|
* that an RTOS must be able to perform efficiently. In ChibiOS/RT there are
|
|
* several mechanisms that can be used, often each mechanism is best suited
|
|
* in a specific scenario.
|
|
*
|
|
* <h2>Synchronously waking up a specific thread</h2>
|
|
* A common situation is to have to synchronously wake up a specific thread.
|
|
* This can be accomplished without the use of any specific synchronization
|
|
* primitive, it uses the very efficient low level scheduler APIs, note that
|
|
* you can also optionally send a simple message from the IRQ handler to
|
|
* the thread.
|
|
* @code
|
|
static Thread *tp = NULL;
|
|
|
|
void mythread(void *p) {
|
|
|
|
while (TRUE) {
|
|
msg_t msg;
|
|
|
|
// Waiting for the IRQ to happen.
|
|
chSysLock();
|
|
tp = chThdSelf();
|
|
chSchGoSleepS(PRSUSPENDED);
|
|
msg = chThdSelf()->p_rdymsg; // Retrieving the message, optional
|
|
chSysUnlock();
|
|
// Perform processing here.
|
|
}
|
|
}
|
|
|
|
CH_IRQ_HANDLER(myIRQ) {
|
|
CH_IRQ_PROLOGUE();
|
|
|
|
// Wakes up the thread.
|
|
chSysLockFromIsr();
|
|
if (tp != NULL) {
|
|
tp->p_rdymsg = (msg_t)55; // Sending the message, optional
|
|
chSchReadyI(tp);
|
|
tp = NULL;
|
|
}
|
|
chSysUnlockFromIsr().
|
|
|
|
CH_IRQ_EPILOGUE();
|
|
}
|
|
* @endcode
|
|
*
|
|
* <h2>Synchronously waking up one of the waiting threads</h2>
|
|
* Lets assume you have a queue of waiting threads, you want to wake up
|
|
* the threads one by one in FIFO order, if there are no waiting threads
|
|
* then nothing happens.<br>
|
|
* This can be accomplished using a @p Semaphore object initialized to zero:
|
|
* @code
|
|
CH_IRQ_HANDLER(myIRQ) {
|
|
CH_IRQ_PROLOGUE();
|
|
|
|
// If there is at least one waiting thread then signal it.
|
|
chSysLockFromIsr();
|
|
if (chSemGetCounterI(&mysem) < 0)
|
|
chSemSignalI(&mysem);
|
|
chSysUnlockFromIsr().
|
|
|
|
CH_IRQ_EPILOGUE();
|
|
}
|
|
* @endcode
|
|
*
|
|
* <h2>Synchronously waking up all the waiting threads</h2>
|
|
* In this scenario you want to synchronously wake up all the waiting threads,
|
|
* if there are no waiting threads then nothing happens.<br>
|
|
* This can be accomplished using a @p Semaphore object initialized to zero:
|
|
* @code
|
|
CH_IRQ_HANDLER(myIRQ) {
|
|
CH_IRQ_PROLOGUE();
|
|
|
|
// Wakes up all the threads waiting on the semaphore.
|
|
chSysLockFromIsr();
|
|
chSemResetI(&mysem);
|
|
chSysUnlockFromIsr().
|
|
|
|
CH_IRQ_EPILOGUE();
|
|
}
|
|
* @endcode
|
|
*
|
|
* <h2>Asynchronously waking up a specific thread</h2>
|
|
* If you have to asynchronously wake up a specific thread then a simple
|
|
* event flags can be used.
|
|
* @code
|
|
static Thread *tp;
|
|
|
|
void mythread(void *p) {
|
|
|
|
tp = chThdSelf();
|
|
while (TRUE) {
|
|
// Checks if an IRQ happened else wait.
|
|
chEvtWaitAny((eventmask_t)1);
|
|
// Perform processing here.
|
|
}
|
|
}
|
|
|
|
CH_IRQ_HANDLER(myIRQ) {
|
|
CH_IRQ_PROLOGUE();
|
|
|
|
// Wakes up the thread.
|
|
chSysLockFromIsr();
|
|
chEvtSignalI(tp, (eventmask_t)1);
|
|
chSysUnlockFromIsr().
|
|
|
|
CH_IRQ_EPILOGUE();
|
|
}
|
|
* @endcode
|
|
*
|
|
* <h2>Asynchronously waking up one or more threads</h2>
|
|
* By using event sources it is possible to asynchronously wake up one or more
|
|
* listener threads. The mechanism requires a single initialized
|
|
* @p EventSource object, all the threads registered as listeners on the
|
|
* event source will be broadcasted.
|
|
* @code
|
|
CH_IRQ_HANDLER(myIRQ) {
|
|
CH_IRQ_PROLOGUE();
|
|
|
|
// Pends an event flag on all the listening threads.
|
|
chSysLockFromIsr();
|
|
chEvtBroadcastI(&my_event_source);
|
|
chSysUnlockFromIsr().
|
|
|
|
CH_IRQ_EPILOGUE();
|
|
}
|
|
* @endcode
|
|
*/
|