?? task.h
字號:
/*
* Copyright (C) 2007-2008 Chris O'Byrne
*
* This file is part of YAVRTOS (see http://www.chris.obyrne.com/yavrtos/)
*
* YAVROTS is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* YAVROTS 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with YAVROTS. If not, see <http://www.gnu.org/licenses/>.
*
* The author can be contacted at <chris@obyrne.com>
*
* version 1.7, 2008 Mar 01
*/
#ifndef TASK_H_
#define TASK_H_
#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdint.h>
#include <stdlib.h>
/**
* \brief Structure describing a semaphore
*
* \sa semaphore
*/
typedef struct semaphorestruct {
/** \brief \internal The value of the semaphore
*
* If you do decide to read or write the value of the semaphore directly, keep in mind
* \li it is a 16-bit value, and so interrupts need to be disabled when reading/writing
* \li after writing, a yield() may need to be performed
*/
int16_t value;
} semaphore_t;
/**
* \brief Structure describing mailboxes
*
* \sa mailbox
*/
typedef struct mbox_struct {
/// \internal The address of the current mailbox data
void *data;
/// \internal Readers wait on this semaphore. This semaphore's value is the "version" of the mailbox data
semaphore_t reading_semaphore;
/// \internal Writers wait on this semaphore for readers to finish reading, or for readers to suspend themselves on the mailbox
semaphore_t writing_semaphore;
} mailbox_t;
// mutexstruct is used in taskstruct, and taskstruct is used in mutexstruct!
struct mutexstruct;
/**
* \brief Structure describing a task
*
* \sa task
*/
typedef struct taskstruct {
/// \internal Pointer to the top of the task stack
uint8_t *stack;
/// \internal Length of the task stack
uint16_t stacklen;
/// \internal The stack pointer as at the last time this task was suspended
uint8_t *sp;
/// \internal The task priority
uint8_t pri;
/// \internal The function that performs the tasks' activities
void (*proc)(void*);
/// \internal The function to execute when the task is stopping
void (*cleanup)();
/// \internal The first mutex this task owns
struct mutexstruct *owned_mutex;
/// \internal The mailbox we are waiting on or reading from
mailbox_t *waiting_on_mbox;
/**
* \brief \internal The version of the mailbox that we are reading or waiting on
*
* If this value is greater than the mailbox version, then we are waiting, otherwise we are reading
*/
int16_t waiting_on_mbox_version;
/// \internal The semaphore the task is waiting on
semaphore_t *waiting_semaphore;
/// \internal The value of the semaphore that the task is waiting on
int16_t waiting_semaphore_min_value;
/**
* \brief \internal Task status
*
* \sa taskstategroup
*/
uint8_t status;
/// \internal Pointer to the next task in the list
struct taskstruct *next;
} task_t;
/**
* \brief A structure describing a mutex
*
* \sa mutex
*/
typedef struct mutexstruct {
/// \internal The task that currently owns the mutex
struct taskstruct *owner;
/// \internal The semaphore that other tasks waiting to own the mutex will suspend themselves against
semaphore_t notification;
/// \internal The next mutex that is owned by this task - used to release mutexes owned by a stopping task
struct mutexstruct *next;
} mutex_t;
/**
* \ingroup task
*
* \brief The current task.
*
* This value may be used in stop_task() to stop the current task
*/
extern task_t *current_task;
/**
* \ingroup task
*
* \brief Tasks are kept in a linked list in memory - this function reserves an "empty" task on that list, ready to be
* subsequently utilised by a call to create_task().
*
* \attention reserve_task() calls malloc(), and disables interrupts during the call to malloc(). Therefore,
* depending on the amount of time your malloc() algorithm takes, and depending on what your time margin for the
* launch of ISRs is, reserve_task() could disable interrupts for "too long". See the description of the
* \c memory_mutex argument, and \ref malloc
*
* The arguments are
* \arg \c stacklen - the number of bytes to reserve for the task stack. Note that it must be long enough to hold a copy
* of the value of all 32 registers when a task switch occurs, and it needs a couple of extra bytes to handle
* interrupts, function calls etc.
* \arg \c pri - the priority of the task. The highest-priority available task will "hog" the CPU. If the task has a priority
* of zero (an "idle" task), then it may not suspend itself on a mutex, semaphore or mailbox.
* \arg \c memory_mutex - if not null, and if called from within a non-idle task, this mutex will be locked on to during the
* call to malloc(), otherwise interrupts will be disabled during the call to malloc() - see \ref malloc.
*/
task_t *reserve_task(uint16_t stacklen, uint8_t pri, mutex_t *memory_mutex);
/**
* \ingroup task
*
* \brief Create a task, ready to be run.
*
* \attention create_task() can call malloc(), and disables interrupts during any call to malloc(). Therefore,
* depending on the amount of time your malloc() algorithm takes, and depending on what your time margin for the
* launch of ISRs is, create_task() could disable interrupts for "too long". See the description of the
* \c memory_mutex argument, and \ref malloc
*
* The arguments are
* <ul>
* <li> \c proc - this is the task function. Upon entry, interrupts will be enabled, and the value of the argument will
* be that of \c init_data. This function does not need to
* contain an infinite loop - if it returns (and if the priority is greater than zero), a stop will be
* performed on the task.</li>
* <li> \c cleanup - this is the task cleanup function, which will be called as the task is dying. May be null if the task
* doesn't need to clean up after itself. See task_stopper()</li>
* <li> \c init_data - this is the value that will be given in the argument to \c proc when it starts</li>
* <li> \c stacklen - the number of bytes to reserve for the task stack. Note that it must be long enough to hold a copy
* of the value of all 32 registers when a task switch occurs, and it needs a couple of extra bytes to handle
* interrupts, function calls etc.</li>
* <li> \c pri - the priority of the task. The highest-priority available task will "hog" the CPU. If the task has a priority
* of zero (an "idle" task), then it may not suspend itself on a mutex, semaphore or mailbox, and it may not be stopped.</li>
* <li> \c memory_mutex - if not null, and if called from within a non-idle task, this mutex will be locked on to during the
* call to malloc(), otherwise interrupts will be disabled during the call to malloc() - see \ref malloc</li>
* </ul>
*
* This function will scan through the task list in memory, looking for one that is not being used with a matching pri and a stacklen
* that is at least as large as that required. If it cannot find such a task, it will create a brand new one. See reserve_task()
*/
task_t *create_task(void (*proc)(void*), void (*cleanup)(), void *init_data, uint16_t stacklen, uint8_t pri, mutex_t *memory_mutex);
/**
* \ingroup task
*
* \brief Stop a task.
*
* The arguments are
* \arg \c t the task to stop
* \arg \c wait_for_mutexes - if not zero, the task will not be stopped until it has released all of its mutexes. If zero,
* then the tasks will stop immediately. Note that if you are using a memory mutex, you \b must set
* \c wait_for_mutexes when stopping any task that uses the memory mutex - see \ref malloc
*
* This function can be called by the current task (stop_task(current_task, ...)), or by a higher-priority task, or by an ISR,
* and may be called on any non-zero-priority task.
*
* If this function is called by the current task, and \c wait_for_mutexes isn't set, then the "cleaning up" of the task
* (task_stopper()) will start executing immediately - i.e. the stop_task(current_task, 0) call won't "return". If
* \c wait_for_mutexes is set, then the lock_off() that releases the tasks' last mutex won't return - the task_stopper()
* will run instead.
*
* If this function is called by a higher priority task, then it will not return until the task in question has
* completely stopped executing.
*
* If this function is called from within an ISR, it will return immediately.
*
* A return value of -1 means that the calling task doesn't have the required permission. A return value of -2 means
* that an attempt was made to stop a zero-priority task. A return value of zero indicates success, and a return value of
* 1 means that the task was already stopped.
*
* See task_stopper() for a description of what happens to the task that is being stopped.
*/
int8_t stop_task(task_t *t, uint8_t wait_for_mutexes);
/**
* \ingroup task
*
* \brief Stop executing the current task and try and execute a higher-priority task or another task of the same priority.
*
* Note that all API calls that could theoretically cause a higher-priority task to be re-enabled will
* call yield(), which will cause an automatic and immediate task switch to that higher-priority task. (This
* also means that if there is another task of the same priority that hasn't been disabled, all such API
* calls will cause a task switch to that task).
*
* Obviously this function can only be called by tasks, and it will "return" the next time it is the turn of the
* current task to execute.
*/
void yield() __attribute__ ((naked));
/**
* \ingroup mutex
*
* \brief Lock on a mutex.
*
* The return value is zero for success.
*
* Since the task may be suspended while waiting for another task to release the mutex, this function may only be
* called by tasks with a non-zero priority.
*/
uint8_t lock_on(mutex_t *m);
/**
* \ingroup mutex
*
* \brief Unlock a mutex.
*
* This function can only usefully be called by the task that locked the mutex in the first place, and it
* may end up yielding control to a task that is waiting on the mutex. The return value is zero for success.
*
* Note that if someone has called stop_task() on this task with the \c wait_for_mutexes parameter set, and if the
* call to this function is the one that releases the tasks' last mutex, then this function won't "return" - the
* task_stopper() will run on this task instead.
*/
uint8_t lock_off(mutex_t *m);
/**
* \ingroup mailbox
*
* \brief Get the current version of a mailbox
*
* This function can be called by an ISR, by any task, or even before the RTOS starts
*/
int16_t get_current_mbox_version(mailbox_t *mbox);
/**
* \ingroup mailbox
*
* \brief Wait for a mailbox to reach at least a certain version, and then start reading from it
*
* \attention It is \b vital that release_mbox_read() be called to release the mailbox for other tasks to write to
* it when the calling task has finished reading the mailbox data
*
* The arguments are
* \arg \c mbox - the mailbox to read from
* \arg \c version - the minimum version of the mailbox that we require. Note that the version of the mailbox actually read
* will be written to this address
*
* The return value is a pointer to the mailbox data. Note that a zero return value does not mean that the mailbox
* read failed - it means that the mailbox was empty - and hence release_mbox_read() must still be called.
*
* Since this function can cause a suspension (i.e. if the mailbox hasn't reached the specified version), it can only be
* called from a task with a non-zero priority
*/
void *read_mbox_min_version(mailbox_t *mbox, int16_t *version);
/**
* \ingroup mailbox
*
* \brief Read a mailbox
*
* This function reads a mailbox regardless of the version of the mailbox.
*
* \attention It is \b vital that release_mbox_read() be called to release the mailbox for other tasks to write to
* it when the calling task has finished reading the mailbox data
*
* The arguments are
* \arg \c mbox - the mailbox to read
* \arg \c version - if not null, the version of the mailbox will be written to this address
*
* The return value is a pointer to the mailbox data. Note that a zero return value does not mean that the mailbox
* read failed - it means that the mailbox was empty - and hence release_mbox_read() must still be called.
*
* Since this function can cause a suspension (i.e. if the mailbox hasn't reached the specified version), it can only be
* called from a task with a non-zero priority
*/
void *read_mbox(mailbox_t *mbox, int16_t *version);
/**
* \ingroup mailbox
*
* \brief Function to call when finished reading from a mailbox
*
* Since tasks are not allowed to write to a mailbox while there are other tasks reading from it, this function
* \b must be called when a task has finished reading from a mailbox - i.e. it must be called after every call to
* read_mbox() or read_mbox_min_version()
*
* Since a task can only read one mailbox at a time, there are no arguments to this function. The return value is
* the mailbox that the task was reading from, or zero if it was not reading from any mailbox.
*
* Note that calling this function may cause a higher-prioroty task that is waiting to write to the mailbox to be
* scheduled.
*/
mailbox_t *release_mbox_read();
/**
* \ingroup mailbox
*
* \brief Initialise a mailbox - this \b must be called on every mailbox before it is used
*
* The arguments are
* \arg \c mbox - the mailbox to initialise
* \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(). It is acceptable to set this value to zero - though callers to read_mbox()
* and read_mbox_min_version() would need to be aware that the return value could be zero.
* \arg \c version - the initial version of the mailbox
*/
void initialise_mbox(mailbox_t *mbox, void *data, const int16_t version);
/**
* \ingroup mailbox
*
* \brief Write to a mailbox
*
* A write to a mailbox will
* \li wait for all tasks that are reading the mailbox to call release_mbox_read()
* \li if \c wait_for_receivers is not zero, it will wait until there are at least that many tasks that have suspended themselves
* on read_mbox_min_version() while waiting for fresh data to be put into this mailbox. Otherwise, the data that is put into
* the mailbox might end up not being picked up by any task (e.g. if another write is made to the mailbox before
* any task attempts to read from it, that second write will over-write what is put into the mailbox by this function call)
* \li then, the mailbox data will be updated, and the version will be incremented by one
* \li if there are any higher-priority tasks suspended on read_mbox_min_version(), they will start executing
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -