Index

Index. 1

1. Introduction. 2

2. Hardware. 2

3. Global data. 3

3.1 Central table. 3

3.2 Constants. 4

3.3 Data types. 4

4. Tasks. 4

4.1 Basics. 4

4.2 Task control block. 4

4.3 Task creation. 5

4.4 Stacks. 6

4.5 Null task. 6

4.6 Programming example. 6

5. Scheduling. 7

5.1 Scheduler 7

5.2 Dispatcher 7

6. Inter Task Communication. 8

6.1 Semaphores. 8

6.2 Mailboxes. 9

6.3 Message queues. 10

7. Interrupt handling. 10

7.1 Basics. 10

7.2 Generic ISR.. 10

7.3 Timer0. 10

8. System function reference. 11

8.1 aos_task_create() 11

8.2 aos_sleep() 11

8.3 aos_uptime() 12

8.4 aos_sem_create() 12

8.5 aos_wait() 13

8.6 aos_signal() 13

8.7 aos_mbox_create() 14

8.8 aos_mbox_send() 14

8.9 aos_mbox_recv() 15

9. References. 15

10. Source code. 16

10.1 aos.h. 16

10.2 core.c. 17

10.3 task.c. 24

10.4 semaphore.c. 25

10.5 mbox.c. 26

10.6 user_task.c. 27


1. Introduction

 

This document describes aOS, an implementation of real-time operating system for Atmel AVR ATmega micro controller family.

 

This software was written to self-study and explore operating system basics.

 

DISCLAIMER: aOS may be used and distributed freely under GNU General Public License (GPL). Author will not take responsibility for any consequencies using this software might cause.

 

2. Hardware

 

aOS is developed with Atmel’s development kit STK300 which has ATmega103L micro controller. Here are some of features ATmega includes:

 


3. Global data

3.1 Central table

 

The central table is a data structure which holds all vital data of operating system. The central table is defined as follows:

 

typedef struct aos_central_table {

   ULONG    aos_systime;       /* Timer ticks since last boot */

   ULONG    aos_idlecntr;      /* Counter for processor idle time */

   UBYTE    aos_version;       /* OS version */

   UBYTE    aos_running;       /* AOS running flag */

   aos_tcb   *tcb_used_list;    /* Pointer to used TCBs */

   aos_tcb   *tcb_free_list;    /* Pointer to TCB freelist */

   aos_tcb   *tcb_current;      /* Pointer to current running task TCB */

   aos_tcb   *tcb_runq;         /* Pointer to highest priority runnable task TCB */

   aos_scb   *scb_used_list; /* Pointer to used semaphores */

   aos_scb   *scb_free_list; /* Pointer to semaphores freelist */

   aos_mcb   *mcb_used_list;    /* Pointer to used mailboxes */

   aos_mcb   *mcb_free_list;    /* Pointer to mailbox freelist */

} aos_cent_tbl;

 

Members of aos_central_table:

 

Member

Type

Description

aos_systime

32-bit unsigned integer

Counter which is incremented in every 30 ms.

aos_idlecntr

32-bit unsigned integer

Counter which is incremented each time when NULL-task is dispached.

aos_version

8-bit unsigned integer

Holds OS version number.

tcb_used_list

TCB pointer

Pointer to first used task control block.

tcb_free_list

TCB pointer

Pointer to the first free task control block.

tcb_current

TCB pointer

Pointer to currently running task’s task control block.

tcb_runq

TCB pointer

Pointer to highest priority runnable task’s task control block.

scb_used_list

SCB pointer

Pointer to first used semaphore control block.

scb_free_list

SCB pointer

Pointer to first free semaphore control block.

mcb_used_list

MCB pointer

Pointer to first used mailbox control block.

mcb_free_list

MCB pointer

Pointer to first free mailbox control block.

 


3.2 Constants

 

There are number of constant values which must be set properly before using aOS. This is because aOS pre-allocates memory for certain global data structures in compilation time. All user configurable constant are located in aos.h file. The values include following:

 

3.3 Data types

 

The aOS has declared following data type in aos.h:

 

typedef unsigned char UBYTE;

typedef unsigned int UWORD;

typedef unsigned long int UDWORD;

typedef unsigned long int ULONG;

 

4. Tasks

4.1 Basics

 

Task is simply a subprogram or function having it’s own operating environment a.k.a. context. The operating system always dispatches the highest priority runnable task and the task will stay on processor until its status changes from runnable by some event or until it is interrupted by higher priority task.

 

All tasks share the same address space. The operating system doesn’t provide a memory protection of any kind. The result is that all functions and global variables, even internals of aOS, are accessible to user tasks. This must be kept in mind when programming with tasks.

 

4.2 Task control block

 

Each task within aOS is assigned to task control block (TCB). The TCB holds all data about a certain task. All used task control blocks are bound together as a linked list. The task control block is defined as follows:

 

typedef struct aos_task_control_block {

   void    *hwstk;                       /* Pointer to task's hardware stack */

   void    *datastk;                     /* Pointer to task's data stack */

   UBYTE    status;                       /* Status of task */

   UBYTE    prio;                         /* Priority of task */

   UWORD    delay;                        /* Nbr of ticks to keep task sleeping */

   struct aos_task_control_block *next_tcb; /* Pointer to next task's TCB */

   void *tcb_sem_q;                      /* Next task on semaphore waiting list */

} aos_tcb;

Members of task control block:

 

Member

Type

Description

hwstk

Pointer

Task’s HW-stack pointer.

datastk

Pointer

Task’s SW-stack pointer.

status

8-bit unsigned integer

Task’s state status. Possible states are RUNNABLE, RUNNING, WAITING and DELAYD.

prio

8-bit unsigned integer

Task’s priority 0-254. Smaller value, higher priority.

delay

16-bit unsigned integer

Counter for clock ticks to keep task sleeping.

next_tcb

TCB pointer

Pointer to next task control block in used TCB list.

tcb_sem_q

TCB pointer

Pointer to next task control block which is waiting for a semaphore.

 

4.3 Task creation

 

All tasks within aOS are created by a system function aos_task_create(). The function is called internally by aOS to create NULL-task and the first user task during system initialisation. The function is also accessible to user tasks in order to create new tasks.

 

The function has four parameters which will deliver all needed information to make new task runnable. The information includes function pointer to task itself, pointers to HW- and SW-stacks and task’s priority.

 

See appendix A for the initial format of HW stack. The HW-stack holds return address to task and all general purpose registers except R28 and R29. On the top, there is SREG with global interrupt enable flag set. The SW-stack is not needed to be formatted, only its start address is saved.

 

Next the function enters its critical section by disabling interrupts. The function tries to get a free Task Control Block (TCB) from TCB-free list. If all TCBs are in use, the function returns with null pointer. Otherwise free TCB is extracted from free list and it is assigned to new task.

 

After successful TCB creation and formatting, the interrupts are enabled and function returns with a pointer to new TCB.

 


4.4 Stacks

 

AVR micro controllers are implemented in such way that there is a need for two separate stacks. Hardware stack is used by interrupt mechanism and subprogram calls to store the return address. HW stack is also used by PUSH and POP commands. Software stack is used to pass parameters to subprograms and receive return values from them. SW stack is also used to store local variables of a subprogram.

 

Each task must have dedicated HW and SW-stack. They are allocated statically in compilation time and their sizes are fixed.

 

4.5 Null task

 

Null-task is a similar task to user tasks but it has some special features. The null-task has the lowest priority and it is always runnable. This causes null-task to be dispatched whenever there is no runnable user task available. Hence the null-task is running when the system is idle.

 

The null task that is used with development board:

 

void NULLTASK( void )

{

   UWORD i;

   while( 1 ) {

      aos_ctbl.aos_idlecntr++;   /* Increase idle time counter */

     LED8 = ON;                   /* Do also some LED blinking */

      for(i=0;i<20000;i++) {};

     LED8 = OFF;

      for(i=0;i<20000;i++) {};

   }

}

 

4.6 Programming example

 

An example of multitask programming can be found in chapter 10, section 10.6. There are four independent tasks: task1, task2, task3 and task4. The first task, task1, is created by aOS in module core.c in function main(). All other task are created by the previously created task. The stacks are also allocated in core.c

 

It can seen that tasks 1, 2 and 3 use a semaphore sem_portb for mutual exclusion. The PORTB in which LEDs are connected is a resource that only one of those three tasks is allowed to access at time.

 

There is also message passing between tasks 2 and 3. Task2 uses system function aos_uptime() to get some data to send to mailbox. Task3 picks up the message and prints it to serial line.

 

Task4 does its own duties only. It doesn’t communicate with other task, it only calls aos_sleep() occasionally. This task has also the lowest priority so it will be pre-empted each time some other task becomes runnable.


5. Scheduling

5.1 Scheduler

 

The scheduler is responsible for deciding which task will be running next. The decision is based on tasks’ status and priorities. The scheduler function is called _aos_schedule(). It’s prefixed with underscore character to differentiate it from user callable system functions.

 

The aOS uses pre-emptive scheduling algorithm. This means that tasks and interrupts can cause an immediate task switch, if a higher priority task becomes runnable as the result of an aOS function. The highest priority task which is runnable is always chosen to be executed next. If the chosen task is not the same task that is currently running, the task have to be swapped. The swap, a.k.a. context switch, is achieved by calling _aos_dispatch() system function.

 

The NULL-task is always made runnable by scheduler. This causes NULL-task to be dispatched if there is no other runnable task in the system.

 

See appendix B for a flow chart of scheduling algorithm.

 

5.2 Dispatcher

 

The dispatcher makes actual switching between tasks. The switching procedure is also known as a context switch. The context switch has three parts:

  1. Saving outgoing task’s registers to HW-stack and saving stack pointers to TCB.
  2. Loading incoming task’s stack pointers from TCB and restoring the registers from HW-stack.
  3. Returning to new task.

 

The dispatcher function is called _aos_dispatch().It’s prefixed with underscore character to differentiate it from user callable system functions.


6. Inter Task Communication

6.1 Semaphores

 

6.1.1 Basics

 

Semaphores provide synchronization and mutual exclusion between tasks. Basically semaphore is a global variable with an initial value from 0 to 65535.

 

6.1.2 Semaphore control block

 

Each semaphore within aOS is assigned to a semaphore control block (SCB). The SCB holds all data about a certain semaphore. All used semaphore control blocks are bound together as a linked list. The semaphore control block is defined as follows:

 

typedef struct aos_sema {

   UWORD              count;        /* Semaphore value */

   aos_tcb            *scb_sem_q;   /* Semaphore queue */

   struct aos_sema      *next_scb;    /* Pointer next semaphore's SCB */

} aos_scb;

 

Members of semaphore control block:

 

Member

Type

Description

count

16-bit unsigned integer

The value of semaphore 0-65535.

scb_sem_q

TCB pointer

Pointer to first task control block waiting for this semaphore.

next_scb

SCB pointer

Pointer to next used semaphore control block.

 

6.1.3 Semaphore creation

 

A semaphore is created with system function aos_sem_create(). The function takes first free Semaphore Control Block (SCB) from SCB free list, initialises its members and returns a pointer of SCB to calling task. The pointer is used as a “handle” when accessing the semaphore. If all SCBs are in use, a null pointer is returned.

 

6.1.4 Semaphore operations

 

Semaphores are manipulated with system functions aos_wait() and aos_signal(). Aos_wait() first tests the value of semaphore. If the value is positive, it is decremented by one and the calling task is resumed. If the value is zero, the calling task is suspended and it is put to semaphore’s wait queue until some other task aos_signal()s the semaphore.

 

Aos_signal() first examines semaphore’s wait-queue. If there is task(s) waiting for semaphore, the first one is de-queued and re-scheduling will occur. If the wait-queue is empty, the semaphore value is simply incremented by one and calling task is resumed.

 

Since semaphore operations are indivisible they should be coded as critical sections. This is implemented by simply disabling interrupts while they are executed.

6.2 Mailboxes

 

6.2.1 Basics

 

Mailbox provides a mechanism to pass different types of messages between tasks. A mailbox is a memory location holding a pointer to actual message.

 

6.2.2 Mailbox control block

 

Each mailbox within aOS is assigned to a mailbox control block (MCB). The MCB holds all data about a certain mailbox. All used mailbox control blocks are bound together as a linked list. The mailbox control block is defined as follows:

 

typedef struct aos_mail_box {

   aos_scb    *msend;                  /* Semaphore for sending to mailbox */

   aos_scb    *mrecv;                  /* Semaphore for receiving from mailbox */

   void       *mmsg;                   /* Pointer to message */

   struct  aos_mail_box *next_mcb;    /* Pointer to next mailbox */

} aos_mcb;

 

Members of mailbox control block:

 

Member

Type

Description

msend

SCB pointer

Semaphore to send a message to mailbox.

mrecv

SCB pointer

Semaphore to receive a message from mailbox.

mmsg

Pointer

Pointer to an actual message.

next_mcb

MCB pointer

Pointer to next used mailbox control block.

 

6.2.3 Mailbox creation

 

A mailbox is created with system function aos_mbox_create(). The function takes first free Mailbox Control Block (MCB), initialises its members and returns a pointer of MCB to task. The pointer is used as a “handle” when accessing the mailbox. If all MCBs are in use, a null pointer is returned. Tasks reading from and writing to mailbox should be aware of type and size of messages they are dealing with.

 

6.2.4 Mailbox operations

 

The mailboxes are accessed with system functions aos_mbox_send() and aos_mbox_recv(). Aos_mbox_send() sends a message to a mailbox unless there is no message already in the mailbox. If the mailbox is occupied, the calling task is suspended until the message is read. Aos_mbox_recv() receives a message and returns a pointer to message to calling task. If the mailbox is empty, the calling task is suspended until a message arrives.

 

 


6.3 Message queues

 

Message queues behave like mailboxes. The major difference is that message queues are FIFO-type arrays and they are capable of holding numerous message-pointers simultaneously. Currently message queues are not implemented in aOS.

 

 

7. Interrupt handling

7.1 Basics

 

Hardware of AVR provides an individual interrupt vectors for each interrupt. In interrupt vector, there is a jump instruction to actual interrupt service routine (ISR). Hence there is no need for dedicated first level interrupt handler (FLIH) which would find out the source of an interrupt and then dispatch related ISR.

 

7.2 Generic ISR

 

Convenient way to implement interrupt handling is to use two level of ISRs. In the first level there is the ISR vectored by hardware. In the second level, there is a high priority user level task, “device driver or second level interrupt handler”, which will do all the interrupt processing. The only thing that first level ISR has to do is to tell second level that an interrupt has occurred.

 

The communication between levels is achieved with a single semaphore. The second level ISR creates the semaphore with initial value of 0 and then calls aos_wait(). This causes task to sleep until an interrupt occurs and first level ISR signals the semaphore by calling aos_signal(). Now the second level ISR is waken up by operating system and it may begin to serve the interrupt.

 

The next step towards real device driver would be to add capabilities for other tasks to communicate with second level ISR. This might be implemented with mailboxes, message queues or even by implementing aos_read() and aos_write() system functions.

 

7.3 Timer0

 

Timer0 has a special role since it is used as system “heartbeat” source. It provides an interrupt in 30 ms intervals. Timer0 ISR is responsible for waking up tasks which have called system function aos_sleep(). The ISR goes through TCB chain and decrements each positive delay counter. If task’s delay counter decrements to zero, the task is made runnable and re-scheduling occurs. Timer0 ISR also increments system uptime counter.

 

 


8. System function reference

8.1 aos_task_create()

 

Description:

 

Create a new task within aOS.

 

Declaration:

 

aos_tcb *aos_task_create( void(*task)(void), UBYTE *hwstk, UBYTE *swstk, UBYTE pri )

 

Parameters:

 

  1. Function pointer to task.
  2. Pointer to task’s hardware stack.
  3. Pointer to task’s software stack.
  4. Task’s priority.

 

Return value:

 

On success, pointer to task’s TCB
On failure, null pointer is returned.

 

8.2 aos_sleep()

 

Description:

 

Put task to sleep for ‘timerticks’ timer ticks. A ‘tick’ is approx. 30 ms.

 

Declaration:

 

void aos_sleep( UWORD timerticks )

 

Parameters:

 

  1. Number of clock ticks for task to sleep.

 

Return value:

 

None

 


8.3 aos_uptime()

 

Description:

 

Get number of timer ticks since system was restarted.

 

Declaration:

 

ULONG aos_uptime( void )

 

Parameters:

 

None

 

Return value:

 

Number of timer ticks since last system restart.

 

8.4 aos_sem_create()

 

Description:

 

Create a new semaphore.

 

Declaration:

 

aos_scb *aos_sem_create( UWORD count )

 

Parameters:

 

  1. The initial value of semaphore.

 

Return value:

 

On success, a pointer to new semaphore.
On failure, a null pointer.


8.5 aos_wait()

 

Description:

 

Issue a wait operation to semaphore. If the value of semaphore is 0, the calling task is suspended by putting it to semaphore’s wait queue.

 

Declaration:

 

void aos_wait( aos_scb *semaphore )

 

Parameters:

 

  1. Pointer to semaphore.

 

Return value:

 

None

 

8.6 aos_signal()

 

Description:

 

Issue a signal operation to semaphore. Increment the value of semaphore by 1. If there are tasks in semaphore’s wait queue, de-queue the oldest one.

 

Declaration:

 

void aos_signal( aos_scb *semaphore )

 

Parameters:

 

  1. Pointer to semaphore.

 

Return value:

 

None

 


8.7 aos_mbox_create()

 

Description:

 

Create a new mailbox.

 

Declaration:

 

aos_mcb *aos_mbox_create( void )

 

Parameters:

 

None

 

Return value:

 

On success, a pointer to new mailbox.
On failure, a null pointer.

 

8.8 aos_mbox_send()

 

Description:

 

Send a message to mailbox. If there is no room in the mailbox, the calling task is suspended until the message is read. The reading task should be aware of the type of message.

 

Declaration:

 

void aos_mbox_send( aos_mcb *mailbox, void *message )

 

Parameters:

 

  1. Pointer to mailbox.
  2. Pointer to message.

 

Return value:

 

None

 


8.9 aos_mbox_recv()

 

Description:

 

Read a message from mailbox. If there the mailbox is empty, the calling task is suspended until a new message is sent to mailbox.

 

Declaration:

 

void *aos_mbox_recv( aos_mcb *mailbox )

 

Parameters:

 

  1. Pointer to mailbox.

 

Return value:

 

Pointer to a message read from the mailbox.

 

 

9. References

 

Labrosse, Jean J., (1992), uC/OS The Real-Time Kernel

Lister, A.M., and Eager, R.D. (1988), Fundamentals of Operating Systems, Fourth Edition

Tanenbaum, Andrew S., (1987), Operating Systems: Design and Implementation

 


10. Source code

10.1 aos.h

/*************************************************************************

This file is part of aOS.

 

aOS 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 2 of the License, or

(at your option) any later version.

 

aOS 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 aOS; if not, write to the Free Software

Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 

Copyright 2001, 2002 Anssi Ylätalo

**************************************************************************/

 

#define AOS_ENTER_CRITICAL      #asm("cli")

#define AOS_EXIT_CRITICAL       #asm("sei")

#define AOS_TASK_RUNNABLE       0b00000000

#define AOS_TASK_RUNNING        0b00000001

#define AOS_TASK_WAITING        0b00000010

#define AOS_TASK_DELAYD         0b00000100

#define AOS_SEM_MAX              12

#define AOS_TASK_MAX             6

#define AOS_MBOX_MAX             5

#define AOS_TASK_HSTK_SIZE      200

#define AOS_TASK_DSTK_SIZE      200

 

typedef unsigned char UBYTE;

typedef unsigned int UWORD;

typedef unsigned long int UDWORD;

typedef unsigned long int ULONG;

 

typedef struct aos_task_control_block {

   void    *hwstk;                          /* Pointer to task's hardware stack */

   void    *datastk;                        /* Pointer to task's data stack */

   UBYTE    status;                          /* Status of task */

   UBYTE    prio;                            /* Priority of task */

   UWORD    delay;                           /* Nbr of ticks to keep task sleeping */

   Struct   aos_task_control_block *next_tcb; /* Pointer to next task's TCB */

   Void    *tcb_sem_q;                         /* Next task on semaphore waiting list */

} aos_tcb;

 

typedef struct aos_sema {

   UWORD    count;             /* Semaphore value */

   aos_tcb   *scb_sem_q;           /* Semaphore queue */

   struct aos_sema *next_scb;    /* Pointer next semaphore's SCB */

} aos_scb;

 

typedef struct aos_mail_box {

   aos_scb   *msend;               /* Semaphore for sending to mailbox */

   aos_scb   *mrecv;               /* Semaphore for receiving from mailbox */

   void    *mmsg;                /* Pointer to message */

   struct aos_mail_box *next_mcb; /* Pointer to next mailbox */

} aos_mcb;

 

typedef struct aos_central_table {

   ULONG    aos_systime;       /* Timer ticks since last boot */

   ULONG    aos_idlecntr;      /* Counter for processor idle time */

   UBYTE    aos_version;       /* OS version */

   UBYTE    aos_running;       /* AOS running flag */

   aos_tcb *tcb_used_list;      /* Pointer to used TCBs */

   aos_tcb *tcb_free_list;      /* Pointer to TCB freelist */

   aos_tcb *tcb_current;      /* Pointer to current running task TCB */

   aos_tcb *tcb_runq;         /* Pointer to highest priority runnable task TCB */

   aos_scb   *scb_used_list;    /* Pointer to used semaphores */

   aos_scb   *scb_free_list;    /* Pointer to semaphores freelist */

   aos_mcb   *mcb_used_list;    /* Pointer to used message mailboxes */

   aos_mcb   *mcb_free_list;    /* Pointer to mailbox freelist */

} aos_cent_tbl;

 

/* Kernel services and data-types that are visible to user tasks */

typedef aos_scb aos_semaphore;

typedef aos_mcb aos_mailbox;

typedef aos_tcb aos_task;

extern aos_tcb *aos_task_create( void (*task)(void), UBYTE *hstack, UBYTE *dstack, UBYTE p );

extern void aos_sleep( UWORD ticks );

extern ULONG aos_uptime( void );

extern aos_scb *aos_sem_create( UWORD ival );

extern void aos_wait( aos_scb *semaphore );

extern UBYTE aos_signal( aos_scb *semaphore );

extern aos_mcb *aos_mbox_create( void );

extern void aos_mbox_send( aos_mcb *mbox, void *msg );

extern void *aos_mbox_recv( aos_mcb *mbox );  

10.2 core.c

/*************************************************************************

This file is part of aOS.

 

aOS 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 2 of the License, or

(at your option) any later version.

 

aOS 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 aOS; if not, write to the Free Software

Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 

Copyright 2001, 2002 Anssi Ylätalo

**************************************************************************/

 

/*********************************************

Chip type           : ATmega103L

Clock frequency     : 4,000000 MHz

Memory model        : Small

Internal SRAM size  : 4000

External SRAM size  : 0

Data Stack size     : 32

*********************************************/

 

#include <mega103.h>

#include <stk300.h>

#include <delay.h>

#include <stdio.h> 

#include "aos.h"

#include "user_tasks.h"

 

aos_cent_tbl aos_ctbl;             /* Central table */

aos_tcb all_tcb[AOS_TASK_MAX];     /* Pointers to Task Control Blocks */

aos_scb   all_scb[AOS_SEM_MAX];    /* Pointers to Semaphore Control Blocks */

aos_mcb   all_mcb[AOS_MBOX_MAX];      /* Pointers to Message Queue Control Blocks */

aos_tcb *NULL_TASK;                 /* Straight pointer to null-process' PCB */

 

UBYTE DNULL[AOS_TASK_DSTK_SIZE], HWNULL[AOS_TASK_HSTK_SIZE];    /* Stacks for NULL task */

UBYTE data_stack1[AOS_TASK_DSTK_SIZE], hw_stack1[AOS_TASK_HSTK_SIZE]; /* Stacks for task1 */

UBYTE data_stack2[AOS_TASK_DSTK_SIZE], hw_stack2[AOS_TASK_HSTK_SIZE]; /* Stacks for task2 */

UBYTE data_stack3[AOS_TASK_DSTK_SIZE], hw_stack3[AOS_TASK_HSTK_SIZE]; /* Stacks for task3 */

UBYTE data_stack4[AOS_TASK_DSTK_SIZE], hw_stack4[AOS_TASK_HSTK_SIZE]; /* Stacks for task4 */

 

/* Low level function which makes actual context switch */

void _aos_dispatch( void )

{

   UBYTE *dstk_highrdy, *hstk_highrdy;

   aos_tcb *curr_tcb;

   AOS_ENTER_CRITICAL; 

  

   curr_tcb = aos_ctbl.tcb_current;              /* Get address to current TCB */

   dstk_highrdy = aos_ctbl.tcb_runq->datastk;  /* Get highest priority ready tasks data stack */     

   hstk_highrdy = aos_ctbl.tcb_runq->hwstk;     /* Get highest priority ready task's hw stack */           aos_ctbl.tcb_current = aos_ctbl.tcb_runq;   /* Set highest priority ready task current */

   aos_ctbl.tcb_current = aos_ctbl.tcb_runq;            /* Make first in run-queue current */

   aos_ctbl.tcb_current->status = AOS_TASK_RUNNING;

  

   /*

     Context switch.

    

;   *dstk_highrdy -> Y+4

;   *hstk_highrdy -> Y+2

;   *curr_tcb    -> Y+0

 

     1. Save HW SP and data SP to current task's PCB

     2. Load SP from highest priority task's TCB

     3. Restore context and return to new task

   */

 

   #asm

     ; Save all registers except SW stack pointer

     PUSH R0

     PUSH R1

     PUSH R2

     PUSH R3

     PUSH R4

     PUSH R5

     PUSH R6

     PUSH R7

     PUSH R8

     PUSH R9

     PUSH   R10

     PUSH   R11

     PUSH   R12

     PUSH   R13

     PUSH   R14

     PUSH   R15

     PUSH   R16

     PUSH   R17

     PUSH   R18

     PUSH   R19

     PUSH   R20

     PUSH   R21

     PUSH   R22

     PUSH   R23

     PUSH   R24

     PUSH   R25

     PUSH   R26

     PUSH   R27

     PUSH   R30

     PUSH   R31

     IN    R0,SREG

      PUSH R0

 

     ; Save current HW stack pointer, low first

     LDD   R26,Y+0       ; hstk_current -> X

     LDD   R27,Y+1

 

     IN    R30,SPL       ; *hstk_current = SP;

     ST    X+,R30

     IN    R30,SPH

     ST    X+,R30

 

     ; Save current SW stack pointer, low first

     MOV   R30,R28       ; LOW(Y) -> R30

     ADIW   R30,6         ; Restore Y value (3 pointers * 2 bytes each)

     ST    X+,R30        ; *dstk_current = Y;

     ST    X,R29

 

     ; Load new HW stack pointer, low first

     LDD   R30,Y+2

     OUT   SPL,R30

     LDD   R30,Y+3

     OUT   SPH,R30

 

     ; Load new SW stack pointer, low first

     LDD   R30,Y+4

     LDD   R31,Y+5

     MOV   R28,R30

     MOV   R29,R31

 

     ; Pop all registers except SW stack pointer

     POP  R0

     OUT   SREG,R0

     POP   R31

     POP   R30

     POP   R27

     POP   R26

     POP   R25

     POP   R24

     POP   R23

     POP   R22

     POP   R21

     POP   R20

     POP   R19

     POP   R18

     POP   R17

     POP   R16

     POP   R15

     POP   R14

     POP   R13

     POP   R12

     POP   R11

     POP   R10

     POP  R9

     POP  R8

     POP  R7

     POP  R6

     POP  R5

     POP  R4

     POP  R3

     POP  R2

     POP  R1

     POP  R0

 

     RETI

   #endasm

}

 

void _aos_schedule( void )

{

   aos_tcb *tptr, *highest_tcb;

   UBYTE highest;

  

   AOS_ENTER_CRITICAL;  

   highest = 255;                             /* NULL-task's priority */

   NULL_TASK->status = AOS_TASK_RUNNABLE;      /* NULL-task is always runnable */

 

   if( aos_ctbl.tcb_current->status & AOS_TASK_RUNNING ) /* If current task is running here */

      aos_ctbl.tcb_current->status = AOS_TASK_RUNNABLE;   /* it is still runnable */

 

   tptr = (aos_tcb *)aos_ctbl.tcb_used_list;     /* Go through all runnable tasks */

   do {                                      /* and get the one having highest priority */

     if( !tptr->status ) {                   /* Is task runnable? */

        if( tptr->prio <= highest ) {   /* Yes, is it's priority higher than highest allready? */

           highest = tptr->prio;           /* Yes, save it */

           highest_tcb = (aos_tcb *)tptr;

        }

     }

     tptr = (aos_tcb *)tptr->next_tcb;

   }

   while( tptr );                              /* Was it last one? */

   aos_ctbl.tcb_runq = (aos_tcb *)highest_tcb;    /* Put highest priority task in run-queue */

   if( aos_ctbl.tcb_current != aos_ctbl.tcb_runq )  /* If next in run-queue is different task */

        _aos_dispatch();                       /* we have to dispatch */

   AOS_EXIT_CRITICAL;

}


#pragma savereg-

 

// Timer 0 output compare interrupt service routine

/* Timer tick ISR */

interrupt [TIM0_COMP] void timer0_comp_isr(void)

{

   volatile aos_tcb *tptr;

   volatile UBYTE sched_n_disp;

 

   AOS_ENTER_CRITICAL;

   if( aos_ctbl.aos_running ) {              /* Is multitasking on? */

      sched_n_disp = 0;                      /* Flag of need for scheduling and dispatching */

     tptr = (aos_tcb *)aos_ctbl.tcb_used_list;     /* Point to first TCB */

     do {                                     /* Go through all PCBs */

        if( tptr->delay ) {                     /* Delay for this task? */

           tptr->delay--;                        /* Yes, decrement */      

           if( !tptr->delay )                    /* Was it last tick? */

             tptr->status = AOS_TASK_RUNNABLE; /* Yes, make the task runnable again */

             sched_n_disp = 1;                   /* We have to re-schedule */

        }

        tptr = (aos_tcb *)tptr->next_tcb;       /* Get next TCB */

     }

     while( tptr );                             /* Last in PCB chain? */

 

      aos_ctbl.aos_systime++;                     /* Increment system uptime */

  

     if( sched_n_disp ) {                         /* If some task became runnable */

        _aos_schedule();                       /* we have to re-schedule */

     }

   }

   AOS_EXIT_CRITICAL;

}

#pragma savereg+

 

void _init_free_list( void )

{

   UBYTE i;

   aos_tcb *tcb_this, *tcb_next;

   aos_scb *scb_this, *scb_next;

   aos_mcb *mcb_this, *mcb_next;

    

   AOS_ENTER_CRITICAL;

   /* Process PCBs */

   for(i=0;i<AOS_TASK_MAX-1;i++) {                 /* Link all PCBs in free list */

     tcb_this = (aos_tcb *)&all_tcb[i];

     tcb_next = (aos_tcb *)&all_tcb[i+1];

      tcb_this->next_tcb = (aos_tcb *)tcb_next;

   }

   all_tcb[AOS_TASK_MAX].next_tcb = (aos_tcb *)0;   /* Last PCB in chain */

  

   /* Process SCBs */

   for(i=0;i<AOS_SEM_MAX-1;i++) {                 /* Link all SCBs in free list */

     scb_this = (aos_scb *)&all_scb[i];

     scb_next = (aos_scb *)&all_scb[i+1];

      scb_this->next_scb = (aos_scb *)scb_next;

   }

   all_scb[AOS_SEM_MAX].next_scb = (aos_scb *)0;   /* Last SCB in chain */  

 

   /* Process MCBs */

   for(i=0;i<AOS_MBOX_MAX-1;i++) {                 /* Link all MCBs in free list */

     mcb_this = (aos_mcb *)&all_mcb[i];

     mcb_next = (aos_mcb *)&all_mcb[i+1];

      mcb_this->next_mcb = (aos_mcb *)mcb_next;

   }

   all_mcb[AOS_MBOX_MAX].next_mcb = (aos_mcb *)0;   /* Last MCB in chain */  

 

   AOS_EXIT_CRITICAL;

}

 

void aos_start( void )

{

   if( aos_ctbl.tcb_used_list ) {            /* Is there tasks created? */

      aos_ctbl.aos_running = 1;               /* Start multi-tasking */

   }

}

 


void aos_sleep( UWORD ticks )

{

   AOS_ENTER_CRITICAL;

   aos_ctbl.tcb_current->delay = ticks;             /* Put task to sleep for 'ticks' time. */

   aos_ctbl.tcb_current->status = AOS_TASK_DELAYD;

   _aos_schedule();

   AOS_EXIT_CRITICAL;

}

 

ULONG aos_uptime( void )

{

   return( aos_ctbl.aos_systime );

}

 

void NULLTASK( void )

{

   UWORD i;

   while( 1 ) {

      aos_ctbl.aos_idlecntr++;

     LED8 = ON;

      for(i=0;i<20000;i++) {};

     LED8 = OFF;

      for(i=0;i<20000;i++) {};

   }

}

 

void main(void)

{

 

UBYTE *nt_dstk, *nt_hstk;     /* NULL-task's stacks */

// Input/Output Ports initialization

// Port A

PORTA=0x00;

DDRA=0x00;

 

// Port B

PORTB=0xFF;

DDRB=0xFF;

 

// Port D

PORTD=0xFF;

DDRD=0x00;

 

// Port E

PORTE=0x00;

DDRE=0x00;

 

// Timer/Counter 0 initialization

// Clock source: System Clock

// Clock value: 3,906 kHz

// Mode: Output Compare

// OC0 output: Disconnected

// Timer/Counter 0 is cleared on compare match

TCCR0=0x0F;

ASSR=0x00;

// 3906 Hz = 0.256 ms -> 120 x 0.256 ms ~ 30 ms / timer tick

TCNT0=0x00;

OCR0=120;

 

// Timer/Counter 1 initialization

// Clock source: System Clock

// Clock value: Timer 1 Stopped

// Mode: Output Compare

// OC1A output: Discon.

// OC1B output: Discon.

// Noise Canceler: Off

// Input Capture on Falling Edge

TCCR1A=0x00;

TCCR1B=0x00;

TCNT1H=0x00;

TCNT1L=0x00;

OCR1AH=0x00;

OCR1AL=0x00;

OCR1BH=0x00;

OCR1BL=0x00;

 

// Timer/Counter 2 initialization

// Clock source: System Clock

// Clock value: Timer 2 Stopped

// Mode: Output Compare

// OC2 output: Disconnected

TCCR2=0x00;

TCNT2=0x00;

OCR2=0x00;

 

// External Interrupt(s) initialization

// INT0: Off

// INT1: Off

// INT2: Off

// INT3: Off

// INT4: Off

// INT5: Off

// INT6: Off

// INT7: Off

EIMSK=0x00;

EICR=0x00;

 

// Timer(s)/Counter(s) Interrupt(s) initialization

TIMSK=0x02;

 

// UART initialization

// Communication Parameters: 8 Data, 1 Stop, No Parity

// UART Receiver: On

// UART Transmitter: On

// UART Baud rate: 9600

 UCR=0x18;

 UBRR=0x19;

 

// Analog Comparator initialization

// Analog Comparator: Off

// Analog Comparator Input Capture by Timer/Counter 1: Off

ACSR=0x80;

 

/* Initialize PCB & SCB freelist */

_init_free_list();

 

/* Set up AOS central table */

aos_ctbl.aos_running = 0;                        /* Not running */

aos_ctbl.tcb_used_list = (aos_tcb *)0;             /* No created processes yet */

aos_ctbl.tcb_free_list = (aos_tcb *)&all_tcb[0];  /* First TCB in free list */

aos_ctbl.scb_used_list = (aos_scb *)0;             /* No created semaphores yet */

aos_ctbl.scb_free_list = (aos_scb *)&all_scb[0];  /* First SCB in free list */

aos_ctbl.mcb_used_list = (aos_mcb *)0;             /* No created mailboxes yet */

aos_ctbl.mcb_free_list = (aos_mcb *)&all_mcb[0];  /* First MCB in free list */

 

/* Allways create NULL task which is allways runnable */

/* and which has the lowest priority */

NULL_TASK = aos_task_create( NULLTASK, HWNULL, DNULL, 255 );

 

/* Use NULL-task's stacks prior to first context switch */

nt_dstk = (UBYTE *)NULL_TASK->datastk;

nt_hstk = (UBYTE *)NULL_TASK->hwstk;

#asm

     ; Load new HW stack pointer, low first

     LDD   R30,Y+0

     OUT   SPL,R30

     LDD   R30,Y+1

     OUT   SPH,R30

 

     ; Load new data stack pointer, low first?

     LDD     R30,Y+2

     LDD     R31,Y+3

     MOV     R28,R30

     MOV     R29,R31

 

#endasm

 

aos_ctbl.tcb_current = (aos_tcb *)NULL_TASK;

 

/***********************************************/

/*                                           */

/* THE FIRST USER TASK IS CREATED HERE.      */

/* Modify according to your needs.              */

/*                                           */

/***********************************************/

aos_task_create( task1, hw_stack1, data_stack1, 50 );

 

PORTB=0xFF;

 

/* Start multitasking */

aos_start();

_aos_schedule();

 

/* This causes program to jump actual NULL process */

#asm

     POP  R0

     OUT   SREG,R0

     POP   R31

     POP   R30

     POP   R27

     POP   R26

     POP   R25

     POP   R24

     POP   R23

     POP   R22

     POP   R21

     POP   R20

     POP   R19

     POP   R18

     POP   R17

     POP   R16

     POP   R15

     POP   R14

     POP   R13

     POP   R12

     POP   R11

     POP   R10

     POP  R9

     POP  R8

     POP  R7

     POP  R6

     POP  R5

     POP  R4

     POP  R3

     POP  R2

     POP  R1

     POP  R0

    

     RETI

  

#endasm

while (1)

{

   /* We won't never, ever reach this point */

   #asm("nop");

};

}


10.3 task.c

/*************************************************************************

This file is part of aOS.

 

aOS 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 2 of the License, or

(at your option) any later version.

 

aOS 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 aOS; if not, write to the Free Software

Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 

Copyright 2001, 2002 Anssi Ylätalo

**************************************************************************/

 

#include "aos.h"

 

aos_tcb *aos_task_create( void (*task)(void), UBYTE *hstack, UBYTE *dstack, UBYTE p )

{

   UBYTE *stk, *dstk, *hstk;

   UBYTE i;

   aos_tcb *tcb_new;

 

   AOS_ENTER_CRITICAL;

   tcb_new = (aos_tcb *)aos_ctbl.tcb_free_list;          /* Get next free TCB */

   if( tcb_new ) {                                    /* TCBs left? */

     /* Set up the hardware stack */

     hstk = (UBYTE *)hstack + (AOS_TASK_HSTK_SIZE - 1);   /* Stack begins in the end of array */

     stk = hstk;

     *stk-- = (UBYTE)(task);             /* Low part of task's address */

     *stk-- = (UBYTE)(task >> 8);        /* High part of task's address */

      for(i=0;i<30;i++)                   /* Clear all other registers */

        *stk-- = 0;

     *stk-- = 0x80;                   /* SREG with interrupts enabled */

  

     /* Set up data stack */

     dstk = (UBYTE *)dstack + (AOS_TASK_DSTK_SIZE - 1);   /* Stack begins in the end of array */

 

      aos_ctbl.tcb_free_list = (aos_tcb *)tcb_new->next_tcb; /* Release TCB from freelist */

     tcb_new->next_tcb = (aos_tcb *)aos_ctbl.tcb_used_list;   /* Link new TCB into TCB used list */

      aos_ctbl.tcb_used_list = (aos_tcb *)tcb_new;

     tcb_new->datastk = (UBYTE *)dstk;          /* Load data stack pointer to TCB */

     tcb_new->hwstk = (UBYTE *)stk;            /* Load hw stack pointer to TCB */ 

     tcb_new->status = AOS_TASK_RUNNABLE;      /* Make task runnable */

     tcb_new->prio = (UBYTE)p;                   /* Set task's priority */

     tcb_new->delay = (UWORD)0;                   /* No initial delay */

     tcb_new->tcb_sem_q = (aos_tcb *)0;         /* Initialize semaphore queue */

   }

  

   AOS_EXIT_CRITICAL;

   return( tcb_new );

}

 


10.4 semaphore.c

/*************************************************************************

This file is part of aOS.

 

aOS 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 2 of the License, or

(at your option) any later version.

 

aOS 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 aOS; if not, write to the Free Software

Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 

Copyright 2001, 2002 Anssi Ylätalo

**************************************************************************/

 

aos_scb *aos_sem_create( UWORD init_val )

{

   aos_scb *scb_new;

  

   AOS_ENTER_CRITICAL;     

   scb_new = (aos_scb *)aos_ctbl.scb_free_list;                /* Get next free SCB */

   if( scb_new ) {                                          /* SCBs left? */

      aos_ctbl.scb_free_list = (aos_scb *)scb_new->next_scb; /* Release SCB from freelist */

     scb_new->next_scb = (aos_scb *)aos_ctbl.scb_used_list;   /* Link new SCB into SCB used list */

      aos_ctbl.scb_used_list = (aos_scb *)scb_new;

     scb_new->count = init_val;                           /* Set semaphore's initial value */

     scb_new->scb_sem_q = (aos_tcb *)0;                  /* No task pending on semaphore */ 

   }

   AOS_EXIT_CRITICAL;

   return( scb_new );

}

 

   /*

     About wait(), signal() and HW locking:

    

     Since we are in uniprocessor environment, indivisibility is

     achieved by simply disabling interrupts. After that there is

     no way for task to loose the processor. Otherwise somekind of

     hardware locking would be needed. In multi-processor environments

      possibilities are 'test-and-set'-instruction or instruction to

      interchange two memory locations.

   */

 

void aos_wait( aos_scb *sem )

{

   aos_tcb *ptcb;

  

   AOS_ENTER_CRITICAL;

   if( sem->count )   {                              /* Semaphore value >0 ? */

     sem->count--;                                 /* Yes, decrement the value */

   } 

   else {                                    /* No, we have to wait */

     ptcb = (aos_tcb *)sem->scb_sem_q;

     if( ptcb ) {                            /* Any tasks in queue? */

        while( ptcb->tcb_sem_q ) {             /* Yes, find queue's end because */

           ptcb = (aos_tcb *)ptcb->tcb_sem_q;   /* this is a FIFO, not a stack. */

        }

        ptcb->tcb_sem_q = (aos_tcb *)aos_ctbl.tcb_current; /* Link current process to wait queue */

     }

     else {

        sem->scb_sem_q = (aos_tcb *)aos_ctbl.tcb_current; /* No, this is the first one */

     }

      aos_ctbl.tcb_current->tcb_sem_q = (aos_tcb *)0;      /* Terminate queue properly */

      aos_ctbl.tcb_current->status = AOS_TASK_WAITING;    /* Now we're waiting */

      _aos_schedule();                                  /* Re-schedule run-queue */

   }

   AOS_EXIT_CRITICAL;

}

 

UBYTE aos_signal( aos_scb *sem )

{

   aos_tcb *ptcb;

 

   AOS_ENTER_CRITICAL;

   ptcb = (aos_tcb *)sem->scb_sem_q;

   if( ptcb ) {                                   /* Is there pending wait()s? */

     sem->scb_sem_q = (aos_tcb *)ptcb->tcb_sem_q;      /* Yes, dequeue the first one */

     ptcb->status = AOS_TASK_RUNNABLE;            /* Make task runnable */

      _aos_schedule();                            /* Re-schedule run-queue */

   } 

   else {                                         /* No, just increment the semaphore */

     sem->count++;

   }

   AOS_EXIT_CRITICAL;

}

10.5 mbox.c

/*************************************************************************

This file is part of aOS.

 

aOS 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 2 of the License, or

(at your option) any later version.

 

aOS 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 aOS; if not, write to the Free Software

Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 

Copyright 2001, 2002 Anssi Ylätalo

**************************************************************************/

 

#include "aos.h"

 

/* Create and initialize a mailbox */

aos_mcb *aos_mbox_create( void )

{

   aos_mcb   *mcb_new;

 

   mcb_new = 0;

   AOS_ENTER_CRITICAL;

   mcb_new = (aos_mcb *)aos_ctbl.mcb_free_list;             /* Save pointer to next free mcb */

   if( mcb_new ) {                                       /* mcbs left? */

      aos_ctbl.mcb_free_list = (aos_mcb *)mcb_new->next_mcb; /* Yes, release mcb from freelist */

     mcb_new->next_mcb = (aos_mcb *)aos_ctbl.mcb_used_list;   /* Link new mcb into mcb used list */

      aos_ctbl.mcb_used_list = (aos_mcb *)mcb_new;

   mcb_new->mrecv = (aos_scb *)aos_sem_create( 0 );  /* Semaphore for receiving messages */

   mcb_new->msend = (aos_scb *)aos_sem_create( 1 );  /* Semaphore for sending messages */

     mcb_new->mmsg      = (void *)0;               /* Clear message pointer */

   }

   AOS_EXIT_CRITICAL;

   return( mcb_new );

}

 

/* Send a message to mailbox */

void aos_mbox_send( aos_mcb *mcb, void *msg )

{

   aos_wait( (aos_scb *)mcb->msend );   /* Is there room in the mailbox */

   mcb->mmsg = msg;                     /* Put message in the mailbox */      

   aos_signal( (aos_scb *)mcb->mrecv ); /* Signal the queue's receive semaphore */

}

 

/* Receive a message from mailbox */

void *aos_mbox_recv( aos_mcb *mcb )

{

   void *msg;

  

   aos_wait( (aos_scb *)mcb->mrecv );   /* Wait for a message */

   msg = mcb->mmsg;                      /* Get the message from mailbox */

   aos_signal( (aos_scb *)mcb->msend ); /* Signal the queue's sending semaphore */ 

 

   return( msg );  

}

10.6 user_task.c

/*************************************************************************

This file is part of aOS.

 

aOS 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 2 of the License, or

(at your option) any later version.

 

aOS 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 aOS; if not, write to the Free Software

Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 

Copyright 2001, 2002 Anssi Ylätalo

**************************************************************************/

 

#include "aos.h"

#include <stdlib.h>

#include <string.h>

 

aos_semaphore *sem_portb;        /* Semaphore for accessing PORTB */

aos_mailbox *pipe;             /* Mailbox for message passing */

UBYTE foomsg[20];                 /* Buffer for messages */

 

void task1( void )

{

   sem_portb = (aos_semaphore *)aos_sem_create( 1 );        /* Mutex for writing PORTB */

 

   aos_task_create( task2, hw_stack2, data_stack2, 100 );  /* Create task2 */

   printf("Task1: %x\r", aos_ctbl.tcb_current );

   while(1)

   {

      aos_wait( sem_portb );     /* Wait for access to PORTB */

      PORTB &= ~0x01;           /* Put on LED1 */

       aos_signal( sem_portb );   /* Release PORTB */

       aos_sleep( 2 );          /* Suspend for 2 timer ticks */

      aos_wait( sem_portb );     /* Wait for access to PORTB */

      PORTB |= 0x01;            /* Put off LED1 */

       aos_signal( sem_portb );   /* Release PORTB */

       aos_sleep( 2 );          /* Suspend for 2 timer ticks */

   }

}

 

void task2( void )

{

ULONG time;                                             /* Uptime counter */

   aos_task_create( task3, hw_stack3, data_stack3, 150 );  /* Create task2 */

   pipe = aos_mbox_create();                            /* Create a mailbox */

   printf("Task2: %x\r", aos_ctbl.tcb_current );

   while(1)

   {

      aos_wait( sem_portb );          /* Wait for access to PORTB */

      PORTB &= ~0x02;                /* Put on LED2 */

       aos_signal( sem_portb );       /* Release PORTB */

       aos_sleep( 33 );              /* Suspend for 33 timer ticks */

      aos_wait( sem_portb );          /* Wait for access to PORTB */

      PORTB |= 0x02;                 /* Put off LED2 */

       aos_signal( sem_portb );       /* Release PORTB */

       aos_sleep( 33 );              /* Suspend for 33 timer ticks */

 

     time = aos_uptime();            /* Get system uptime */

     sprintf( foomsg, "%u ", time ); /* Print formatted time to buffer */

       aos_mbox_send( pipe, foomsg );  /* Send buffer to mailbox */

   }

}

 

void task3( void )

{

   UBYTE *msg;

  

   aos_task_create( task4, hw_stack4, data_stack4, 220 );  /* Create task4 */

   printf("Task3: %x\r", aos_ctbl.tcb_current );

   while(1)

   {

     msg = aos_mbox_recv( pipe );     /* Receive a message from mailbox */

     puts( msg );                   /* Print the message */

      aos_wait( sem_portb );          /* Wait for access to PORTB */

      PORTB &= ~0x04;                /* Put on LED3 */

       aos_signal( sem_portb );       /* Release PORTB */

       aos_sleep( 16 );              /* Suspend for 16 timer ticks */

      aos_wait( sem_portb );          /* Wait for access to PORTB */

      PORTB |= 0x04;                 /* Put off LED3 */

       aos_signal( sem_portb );       /* Release PORTB */

       aos_sleep( 16 );              /* Suspend for 16 timer ticks */

   }

}

 

 

void task4( void )

{

   UWORD i;

   ULONG j;

   UBYTE mask;

   printf("Task4: %x\r", aos_ctbl.tcb_current );

   mask = 0x08;                 /* Start with bitmap 00001000 */

   j = 0;

   while(1)

   {

      PORTB &= ~mask;             /* Put on some LED */

      for(i=0; i<50000;i++) {}   /* Some delay. Please notice that

                                    task is not suspended */

      PORTB |= mask;              /* Put off LED */

      for(i=0; i<50000;i++) {}   /* Delay again */

 

      mask <<= 1;                 /* Shift bitmap 1 bit left */

      if( mask & 0x80 )              /* Did we overflow? */

        mask = 0x08;            /* Yes, start from beginning */

       

      if( j++ == 20 ) {             /* Do something in every 20th round */

         aos_sleep( 10 );         /* In this case we suspend for 10 ticks */

        j = 0;

      }

 

   }

}