⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 task.h

📁 yavrtos,一款用于广泛用于AVR单片机的RTOS,文件里是这款OS的源码
💻 H
📖 第 1 页 / 共 2 页
字号:
/*
 * 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 + -