?? yavrtos.dox
字號:
/**
\mainpage YAVRTOS
\htmlonly
<p align="center"><font size="+2"><b><a href="yavrtos.zip">Download YAVRTOS</a></b></font></p>
\endhtmlonly
\section History
YAVRTOS was my Christmas 2007-2008 project. I decided to do it
\li because I wanted a good RTOS for other projects
\li to see if I could write an RTOS
\li because I didn't fully understand the other RTOSes out there
Since understanding is one of my aims, I have documented YAVRTOS in the hope that it will help others who were in the same situation as me.
\section naming What's in a name?
YAVRTOS stands for Yet Another Atmel® AVR® Real-Time Operating System
\section TandC Terms and Conditions
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 \ref LGPL "GNU Lesser General Public License" for more details.
\section prereq Pre-requisites
I've developed this using the WinAVR&tm; compiler (http://winavr.sourceforge.net/), and the AVR® Studio debugger (available from http://www.atmel.com/). I've only tested it against an ATmega32 so far.
\section Status
At present, this project has not been rigorously tested. I've run many tests against the AVR® Studio debugger running
an ATmega32, and everything seems to work. I've started to use it on another project, and I've met with success. This other
project uses mailboxes (which in turn use semaphores) fairly extensively, and it also bit-bang receives serial data at
4800 baud on an 8MHz ATmega32.
Apart from testing, there are some other things I want to do with this project
- more documentation
- at present, malloc() can be used by create_task() and reserve_task(), and interrupts are disabled during the calls to malloc(). The solution I have - a mutex for the microcontrollers' memory - isn't that bad, I think. A more interesting solution would be to write a thread-safe malloc()...
What all this means is that you should check back
\htmlonly
<a href="http://www.chris.obyrne.com/yavrtos/">here</a>
\endhtmlonly
often for updates, and you should check
that YAVRTOS works as expected in your application. If you
find a bug, please email me (my email address is at the bottom of the page).
\section What What is a real-time operating system?
Processors are only actually only able to do one thing at a time, whereas we need them to do more than one thing at a time (e.g. update the display while checking for keypresses while reading the serial port). A real-time operating system (RTOS) provides facilities for achieving that.
\section what-task What is a task?
A task does one of the things (e.g. update the display, check for keypresses, read the serial port) that the application needs done. The real-time operating system allows tasks to execute concurrently, while providing facilities for e.g. tasks to communicate with each other. See the \ref task "task"
page for more information.
\section what-tick What is a tick?
The RTOS needs to be able to switch betwen tasks - they way it does this is by using a regular processor interrupt
(e.g. one of the timer interrupts). Every such interrupt is called a "tick". These interrupts must be set up before the RTOS is started.
See \ref isr for more information.
\section what-scheduler What is the task scheduler?
The task scheduler (or "task switcher") is the central component of the RTOS. It is responsible for ensuring that the tasks are run according to the rules of priority. It runs every tick, and it also runs when anything happens that may change the list of runnable tasks (e.g. a mutex being released, or a semaphore changing value).
When the task scheduler runs, it effectively generates a list of the highest-priority runnable tasks. If one of those tasks has just had control of the processor, it runs the next task in the list, otherwise it runs the first task in the list.
\section what-stack What is the stack?
The stack is the standard stack that the processor implements with its stack pointer. However, when more than one task can be executing at a time, the stack becomes more complicated. In particular, every task must have its own stack, and the size of this stack is specified when the task is created. And, each stack must be large enough to accommodate all the uses the task puts the stack to.
As well as a stack for each task, there is another "system" stack - this stack is used by ISRs, and must also be large enough to accommodate the requirements of the ISRs. If it is possible for more than one ISR to be running concurrently, then the system stack must be large enough to accommodate the requirements of all the ISRs that could be running concurrently.
\section what-stack-uses What are the uses the stack is put to?
The stack is used -
\li To hold the value of local variables. For instance,
\code
void proc() {
char c;
\endcode
The variable "c" will use up one byte of the stack.
\li To hold the return address of function calls.
\code
void proc() {
proc2();
\endcode
The call to proc2() will use up two bytes of the stack.
\li To hold the CPU context on task switch - there must be at least 35 bytes on the stack to handle task switching
\li To hold the return address, and some other information, on interrupt - this takes maybe another 10 bytes of stack
\li As a system scratchpad - maybe another half-dozen bytes of stack.
So a minimum stack size would be about 55 bytes.
\section what-small-stack What happens if the stack isn't big enough?
Memory corruption, and hence a probable spectacular (and untraceable) crash.
\section seealso Further reading
\li \ref task
\li \ref semaphore
\li \ref mutex
\li \ref mailbox
\li \ref Example
\li \ref usage "Using YAVRTOS"
\li \ref howdoI "How do I?"
\li \ref QandA "Questions and Answers"
\li \ref isr
\li \ref api-usage-restrictions "API Usage Restrictions"
*/
/**
\defgroup task Tasks
A task performs a function in the application, and an application can have a number of tasks running "simultaneously". Tasks
are a function with the signature <tt>void task_func(void *init)</tt>, and optionally a "cleanup" function
<tt>void task_cleanup()</tt> that is called when the task is exiting. The value of the \c init argument to the task function
is specified when the task is created.
Tasks are created by create_task(), and can be stopped by stop_task().
Tasks are held in memory in a linked list - spaces may be reserved on that list for future tasks using
reserve_task().
Tasks can suspend themselves when they are unable to do any work (e.g. a task
that reads a microcontroller peripheral could suspend itself when the peripheral has no data available), hence
allowing other tasks that can do work to run, and hence the application as a whole can get the maximum
use of the CPU's processing power. The ways a task can suspend itself are
\li by calling lock_on() on a \ref mutex "mutex" that another task has already locked on to. Note that create_task() and reserve_task() can
decide to lock on to a supplied "memory mutex" - see create_task(), reserve_task() and \ref malloc
\li by calling wait_for_min_value() on a \ref semaphore "semaphore" that hasn't yet reached the specified value
\li by calling wait_for_increment_of() on a \ref semaphore "semaphore"
\li by calling read_mbox_min_version() on a \ref mailbox "mailbox" that hasn't yet reached the specified version
\li by calling write_mbox() - see write_mbox() for the conditions under which a task will be suspended
\li by calling wait_for_receiver() on a \ref mailbox "mailbox" that no-one is waiting to read from
\li a temporary suspension can be caused by calling yield() - if there are other schedulable tasks, they will run, but the calling task will eventually
be re-scheduled to run
\li by waiting for another task to stop - see stop_task() for more information
Every task has a priority. The rule is that the RTOS will not allow the lowest-priority tasks to run until all higher-priority tasks have suspended
themselves. If there are more than one highest-priority not-suspended tasks, then the RTOS will switch between them once
every tick interrupt (and/or whenever one of the tasks calls yield()).
A priority of zero indicates an "idle" task - a task that is run when there are no other tasks that can run. There must always be at least one idle task, and idle tasks can neither be stopped nor can they do anything to suspend themselves (e.g. they cannot wait for a mutex to be released), as the
microcontroller needs at least one task to be always available for scheduling. Usually there is just one idle task that just sleeps the CPU.
A recommended idle task is
\code
void idle_task(void *p) {
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();
sei();
sleep_cpu();
}
\endcode
A task switch can occur
\li whenever anything happens to a mutex, semaphore or mailbox that may cancel the suspension of a higher priority
task (e.g. when a semaphore has its value increased)
\li when a task calls yield()
\li when the system tick interrupt happens - see \ref isr for more information about the tick interrupt.
From the point of view of the RTOS, the microcontroller can be doing one of three things
\li executing code before the RTOS has been started with a call to task_switcher_start(). When the microcontroller is in this state, the RTOS
is obviously unable to "suspend" what the microcontroller is doing, and hence none of the API calls that could cause such a suspension may be
used
\li executing a task, which can be an "idle" task of zero priority, or a "non-idle" task of non-zero priority. When executing an idle task,
a task suspension is disallowed (as there must always be something that the microcontroller can do when it is otherwise unable to do anything).
\li executing an ISR, including the tick interrupt ISR. Again, the RTOS is unable to suspend the ISR.
Therefore there are restrictions on when certain API calls may be made - these restrictions are summarised on the
\ref api-usage-restrictions "api usage restrictions" page.
See the \ref Example "example application" for an example of how to start and run some simple tasks.
Tasks can be stopped by other tasks or by ISRs - see stop_task(). When a task is stopped, mutexes and mailboxes are
released - see task_stopper()
*/
/**
\defgroup semaphore Semaphores
A semaphore contains a number that tasks can suspend themselves against, waiting for the number to
reach at least a certain value before the task is schedulable again.
To update a semaphore's value, use increment_semaphore_by()
To read a semaphore's value, use get_semaphore_value()
To lock on a semaphore, use wait_for_min_value() or wait_for_increment_of()
See \ref usage-sema "using semaphores" for information on how to use semaphores
\defgroup mailbox Mailboxes
Mailboxes are used to pass information between tasks.
Mailboxes contain a pointer to the mailbox data, and a version number. Tasks can both read from and write to mailboxes.
The rules governing mailboxes are
\li A task can only read from one mailbox at a time - the task must finish reading a mailbox before it can start reading
another one
\li A task cannot write to a mailbox while there are other tasks reading from it
\li A mailbox can contain only one "message" at a time
Mailboxes \b must be initialised with initialise_mbox().
Tasks can write to mailboxes with write_mbox() or write_mbox_now(). Tasks can read from mailboxes with
read_mbox() or read_mbox_min_version(), and \b must subsequently call release_mbox_read() when they are done
reading from the mailbox. Other functions governing the use of mailboxes are get_current_mbox_version()
and wait_for_receiver().
Mailboxes can be synchronous or asynchronous. When task A writes to a mailbox, its version number is incremented. If task B
has been waiting for that version of the mailbox, it is immediately marked as reading the mailbox, hence other tasks are
prevented from writing to the mailbox until task B has finished reading from it. Of course, if there wasn't a task B waiting
for the data, then a task C could over-write what task A wrote to the mailbox without any task ever seeing what task A wrote.
To mitigate this problem, task A is able to request that a certain minimum number of tasks be waiting for the mailbox to be
updated before the data is written to the mailbox - hence task A is guaranteed that the data it writes is seen.
It is the responsibility of the task that writes to the mailbox to allocate memory for the mailbox data. To prevent problems
arising from the memory allocation going out of scope before all the reading tasks have finished reading the data, a writing
task is able to request that all reading tasks finish reading before control is passed back to the writing task. Not only that,
but the writing task is also able to request that the mailbox data be "nullified" (i.e. that a new version of the mailbox with
a null data pointer be published) as soon as all the reading tasks have finished reading, thereby ensuring that no subsequent
reading tasks will attempt to read the writing tasks' data.
See \ref usage-mbox "using mailboxes" for information on how to use mailboxes
*/
/**
\defgroup mutex Mutexes
A <b>MUT</b>ual <B>EX</b>clusion object - only one task can "own" a mutex at any one time.
Mutexes are "owned" by calling lock_on(), and released by calling lock_off()
If another task tries to own an "owned" mutex, that task will be
suspended until the mutex becomes available.
Tasks may own as many mutexes as they wish, and mutexes will automatically be released when the task exits
See \ref usage-mutex "using mutexes" for information on how to use mutexes
If malloc() is going to be used by your task, then you may require a mutex for the microcontroller memory -
see \ref malloc
*/
/**
\defgroup isr Interrupt Service Routines
Interrupt Service Routines are functions that are called when an interrupt occurs, and
are defined with the TASK_ISR() macro.
*/
/**
\defgroup taskstategroup The possible states that a task can be in
\internal
These are the states that a task can be in (i.e. the possible values of task.status).
*/
/**
\page Example
In this example, we will flash two LEDs connected to port A at different rates.
\code
#include "task.h"
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sleep.h>
// A semaphore that we will increment at every tick
static semaphore_t tick;
// We will also create a mutex for port A, as both tasks will be using it potentially "simultaneously"
static mutex_t porta_mutex;
// This is our first task - blinking port A bit 0 once every 200 ticks
void blink1(void *p) {
while (1) {
// Obtain a lock on the port A mutex
lock_on(&porta_mutex);
// OK - port A is now all ours, so blink the LED
PORTA ^= 0x01;
// Release our hold on the port A mutex
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -