?? task.h
字號:
* \li if \c wait_for_empty_nullify is not zero, the function will not return until all tasks that had been suspended on
* read_mbox_min_version() have called release_mbox_read().
* \li if \c wait_for_empty_nullify is greater than one, a new version of the mailbox data, containing a null pointer, will be
* published
*
* If the task that writes to the mailbox can be stopped with stop_task(), then you could end up in a situation where the memory
* location of the task data goes out of scope while the mailbox is being read. If this is a possibility, then it is recommended
* that a \ref mutex "mutex" be created for the mailbox, that it be locked on to while the mailbox is being written to, that the
* task only ever be stopped with the \c wait_for_mutexes parameter of stop_task() set, and that
* \c wait_for_empty_nullify be set to at least one (it would need to be set at two if there are more than one tasks that could
* read from the mailbox).
*
* The arguments are
* <ul>
* <li> \c mbox - the mailbox to write to</li>
* <li> \c data - the pointer to the mailbox data. This is the value that will be returned by read_mbox() and
* read_mbox_min_version(), so it is vital that it points to valid data for the lifetime of this particular
* version of the mailbox.</li>
* <li> \c wait_for_receivers - if non-zero, the write to the mailbox will not occur until there are at least
* \c wait_for_receivers task(s) waiting to read data from the mailbox. This could be used if it is
* vital that at least a specified number of
* receiving task(s) receive the information - it would give the receiving task(s) a chance to initialise</li>
* <li> \c wait_for_empty_nullify
* <ul>
* <li>if greater than zero, the function will not return until all tasks that were waiting to read the
* mailbox have done so. This could be used if it is vital that the sender knows that all messages have been received by
* the receiving task(s).</li>
* <li>if greater than one, the mailbox data will be set to null when all the tasks that were waiting to
* read the mailbox have done so (which means that subsequent reads of the mailbox will return a null pointer),
* so the data in the mailbox data buffer can safely be changed</li>
* </ul>
* </ul>
*
* Since this function can cause a task suspension, it can only be called from a task with a non-zero priority
*/
void write_mbox(mailbox_t *mbox, void *data, uint8_t wait_for_receivers, uint8_t wait_for_empty_nullify);
/**
* \ingroup mailbox
*
* \brief Attempt to write to a mailbox
*
* This function will attempt to write to a mailbox - if the mailbox is being read, and is therefore unavailable for writing
* to as per the rules of mailboxes, this function will return
* immediately without having written anything to the mailbox.
*
* The arguments are -
* \arg \c mbox - the mailbox to write to
* \arg \c data - the pointer to the mailbox data. This is the value that will be returned by read_mbox() and
* read_mbox_min_version().
*
* The return value is zero on success
*
* Since this function will never cause a task suspension, it can be called from an ISR, from the idle task, or from anywhere
* else in the application. Also, if this function is called from within a task, and if the task is potentially stoppable
* by another task using stop_task() on it, then it is important that the memory location pointed to by \c data doesn't
* go out of scope when the task is stopped.
*/
int8_t write_mbox_now(mailbox_t *mbox, void *data);
/**
* \ingroup mailbox
*
* \brief Wait for a task to be suspended while trying to read from a mailbox
*
* This function will suspend the caller until another task is suspended while calling read_mbox_min_version(). This could
* be used to give a task that needs to read the mailbox a chance to initialise.
*/
void wait_for_receiver(mailbox_t *mbox);
/**
* \ingroup semaphore
*
* \brief Wait for a semaphore to reach at least a particular value.
*
* Since the calling task could be suspended, this can only be called by a task with a non-zero priority
*/
uint8_t wait_for_min_value(semaphore_t *s, int16_t value);
/**
* \ingroup semaphore
*
* \brief Wait for a semaphore to increment its value by a certain amount
*
* The arguments are
* \arg \c p - the semaphore to wait on
* \arg \c amount - the amount by which the semaphore should increment before returning. Note that semaphore
* values are actually signed 16-bit numbers, so the maximum value for this argument is about 32,000
*
* Since the calling task could be suspended, this can only be called by a task with a non-zero priority
*/
uint8_t wait_for_increment_of(semaphore_t *p, uint16_t amount);
/**
* \ingroup semaphore
*
* \brief Get the current value of a semaphore
*
* This function may be called anywhere
*/
int16_t get_semaphore_value(semaphore_t *s);
/**
* \ingroup semaphore
*
* \brief Increment the value of a semaphore by the given amount.
*
* The arguments are
* \arg \c s - the semaphore to increment
* \arg \c amount - the amount by which the semaphore should be incremented. Note that semaphore
* values are actually signed 16-bit numbers, so the maximum value for this argument is about 32,000
*
* This may be called by any task, by an ISR, or even before the task
* system is running.
*
* When called from a task, this function may end up yielding control to a higher-priority task that is waiting on the
* semaphore.
*/
void increment_semaphore_by(semaphore_t *s, uint16_t amount);
/**
* \ingroup task
*
* \brief Start the whole process running.
*
* Note that this method will never return. The arguments are
* \arg \c idle - the "idle" function (the function to execute when we have nothing else to do)
* \arg \c idle_data - the value of the argument to \c idle when it starts
* \arg \c idle_stacklen - the length of the stack for the "idle" function - see create_task()
* \arg \c system_stacklen - the length of the system stack - this is the stack that will be in use during all ISRs
*
* A recommended idle task is -
* \code
* void idle_task() {
* set_sleep_mode(SLEEP_MODE_IDLE);
* sleep_enable();
* sei();
* sleep_cpu();
* }
* \endcode
*
* See \ref Example "the example application" for an example of how to start the task switcher.
*
* Note that, if you are using the TASK_ISR() macro to give your ISRs access to the system stack (and you should!),
* then all interrupts must remain disabled right up until this function is called. Interrupts will be enabled as soon
* as the first task switch starts.
*/
void task_switcher_start(void (*idle)(void*), void *idle_data, uint16_t idle_stacklen, uint16_t system_stacklen) __attribute__ ((naked));
/**
* \ingroup task
*
* \brief A flag indicating whether interrupts were enabled - used by disable_interrupts() and restore_interrupts()
*/
#define interrupt_store_t uint8_t
/**
* \ingroup task
*
* \brief Disable interrupts system-wide.
*
* Returns a value which is non-zero if interrupts were enabled. Can be called
* from anywhere.
*/
interrupt_store_t disable_interrupts();
/**
* \ingroup task
*
* \brief Restore the state of the system-wide interrupts.
*
* A non-zero argument enables interrupts. Can be called from
* anywhere.
*/
void restore_interrupts(interrupt_store_t interrupts);
/*
* Everything below here is for the benefit of the TICK_ISR(vector) and TASK_ISR(vector,proc) macros
*/
/**
* \brief \internal Structure used by the system to hold the system stack, and a flag indicating whether an ISR is currently
* being executed.
*/
struct system_struct {
/// non-zero if we are executing an ISR. Also, bit 1 is set if a task switch is required.
uint8_t interrupted_task;
/// The top of the system stack
uint8_t *stack_top;
};
/**
* \brief \internal The system_struct used to hold the system stack and a flag indicating whether an ISR is currently being
* executed
*/
extern struct system_struct system;
/**
* \brief \internal Save the CPU context to the stack, and disable interrupts
*/
#define save_cpu_context() __asm__ volatile( \
"push r0\n in r0, 0x3f\n cli\n" \
"push r1\n push r2\n push r3\n push r4\n push r5\n push r6\n push r7\n" \
"push r8\n push r9\n push r10\n push r11\n push r12\n push r13\n push r14\n push r15\n" \
"push r16\n push r17\n push r18\n push r19\n push r20\n push r21\n push r22\n push r23\n" \
"push r24\n push r25\n push r26\n push r27\n push r28\n push r29\n push r30\n push r31\n" \
"push r0\n" ::)
/**
* \brief \internal Restore the CPU context from the stack, possibly re-enabling interrupts
*/
#define restore_cpu_context() __asm__ volatile ( \
"pop r0\n" \
"pop r31\n pop r30\n pop r29\n pop r28\n pop r27\n pop r26\n pop r25\n pop r24\n" \
"pop r23\n pop r22\n pop r21\n pop r20\n pop r19\n pop r18\n pop r17\n pop r16\n" \
"pop r15\n pop r14\n pop r13\n pop r12\n pop r11\n pop r10\n pop r9\n pop r8\n" \
"pop r7\n pop r6\n pop r5\n pop r4\n pop r3\n pop r2\n pop r1\n out 0x3f, r0\n pop r0\n" ::)
// The all-important task switch function
void switch_task() __attribute__ ((naked));
/**
* \ingroup isr
*
* \brief The macro for ISRs
*
* The arguments are
* \arg \c vector the ISR vector
* \arg \c do_task_switch something that evaluates non-zero if the ISR should trigger a task switch. This
* could be a constant, a function call, a macro, anything. Note that the tick interrupt \b must,
* by definition, trigger a task switch. It is highly recommended that this macro always evaluates
* to non-zero - the only time where it would be safe for it to evaluate to zero is if it does
* absolutely nothing to any task, semaphore, mailbox or mutex (i.e. if it doesn't do anything that
* could un-suspend a suspended task).
*
* See \ref Example "the example application" and \ref how-write-isr "how do I write an ISR" for
* examples of usage.
*
* The WinAVR&tm; ISR() macro can also be used to define ISRs - however, when using ISR() as opposed to
* TASK_ISR(), you cannot make use of any YAVRTOS API call from within your ISR, and your ISR will not
* (necessarily) be using the system stack.
*
* Taking this macro apart line-by-line, we have
*
* \code
* // The standard way of calling an ISR
* void vector(void) __attribute__ ((signal,naked,__INTR_ATTRS));
* // The ISR itself
* void vector(void) {
* // Save the entire CPU context to the stack (which could be a task stack or the system stack)
* save_cpu_context();
* // Global interrupts were enabled just before this ISR was launched (otherwise it would not have
* // launched), and are disabled upon entry into the ISR. Set the interrupt enable bit at the location of
* // the saved status register on the saved stack so that when we restore the CPU context, interrupts will
* // be re-enabled.
* *(((uint8_t*)SP)+1) |= _BV(SREG_I);
* // Now, we have either interrupted a task or interrupted another ISR. system.interrupted_task will
* // be non-zero if a task has already been interrupted - i.e. if we have interrupted an ISR
* // Note that task_switch() also sets system.interrupted_task - any ISRs that manage to run during
* // the brief period when task_switch() enables interrupts should not subsequently run task_switch()!
* if (system.interrupted_task) { // We have interrupted an ISR (or we have interrupted task_switch())
* // Execute the macro to see if we should do a task switch
* if (do_task_switch) {
* // The macro may have enabled interrupts - disable them again
* cli();
* // Set bit 1 of system.interrupted_task - the bit that signals that a task switch is
* // required. We don't do the task switch just yet, as we have interrupted another ISR,
* // so we need to return to that ISR first. The ISR that interrupted the task will be the one
* // to actually perform the task switch (see below)
* // If we have actually interrupted task_switch(), then setting bit 1 of system.interrupted_task
* // will have no effect - but we were doing a task switch anyway!
* system.interrupted_task = 3;
* } else {
* // Task switch not required (this time). Make sure that interrupts are still disabled
* cli();
* }
* } else { // We have interrupted a task
* // Save the stack pointer
* current_task->sp = (uint8_t *)SP;
* // Switch to the system stack
* SP = (uint16_t) system.stack_top;
* // Set the system.interrupted_task so that subsequent ISRs will know that they have interrupted
* // an ISR, not a task. This has the side-effect of disabling switch_task() - all API calls check
* // system.interrupted_task and do NOT perform a task switch if it is non-zero.
* system.interrupted_task = 1;
* // Execute the macro and see if we should do a task switch
* if (do_task_switch) {
* // The macro could have enabled interrupts - disable them
* cli();
* // Signal to ourselves that we need a task switch - bit 1 of system.interrupted_task is set
* // when a task switch is required
* system.interrupted_task = 3;
* } else {
* // Make sure that interrupts are disabled
* cli();
* }
* // At this point, any ISRs that interrupted us have finished.
* // Now, if a task switch is required, perform it!
* if (system.interrupted_task & 2) {
* // switch_task() will (eventually) reset system.interrupted_task, and never "returns"
* switch_task();
* }
* // A task switch is not required - restore the stack pointer so that we return to the task
* SP = (uint16_t) current_task->sp;
* // We are about to return to the task - reset system.interrupted_task
* system.interrupted_task = 0;
* }
* // Return to whatever we were doing before this ISR was called
* restore_cpu_context();
* __asm__ volatile ("ret" ::);
* }
* \endcode
*/
#ifdef DOXYGEN
#define TASK_ISR(vector, do_task_switch)
#else
#define TASK_ISR(vector,do_task_switch) \
void vector(void) __attribute__ ((signal,naked,__INTR_ATTRS)); \
void vector(void) { \
save_cpu_context(); \
*(((uint8_t*)SP)+1) |= _BV(SREG_I); \
if (system.interrupted_task) { \
if (do_task_switch) { \
cli(); \
system.interrupted_task = 3; \
} else { \
cli(); \
} \
} else { \
current_task->sp = (uint8_t *)SP; \
SP = (uint16_t) system.stack_top; \
system.interrupted_task = 1; \
if (do_task_switch) { \
cli(); \
system.interrupted_task = 3; \
} else { \
cli(); \
} \
if (system.interrupted_task & 2) { \
switch_task(); \
} \
SP = (uint16_t) current_task->sp; \
system.interrupted_task = 0; \
} \
restore_cpu_context(); \
__asm__ volatile ("ret" ::); \
}
#endif /* DOXYGEN */
#endif /*TASK_H_*/
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -