Chapter 3

Basic Functions and Structures

3.1  Initializing the Library

3.1.1  Functions

void avr_thread_init(void)   function

This function initializes the AVR Threads Library. It must be called before any other AVR Threads Library function is called.

This function sets up a default context so that it can treat the currently running program as a thread. It also prepares the “idle” thread that runs when no other thread can run. After calling this function, task switches can occur, both implicit and explicit.

3.2  Starting, Stopping, and Pausing Threads

3.2.1  Functions

void avr_thread_start(avr_thread_context* context, void (*fn)(void), uint8_t* stack, uint16_t stack_size)   function

This function starts a new thread of execution given a pointer to a avr_thread_context, context; a pointer to a function where the thread will begin execution, fn; a pointer to a stack, stack; and the size of the stack, stack_size.

The memory pointed to by context must remain valid throughout the life of the new thread.

The stack space provided must also remain valid throughout the life of the new thread. The stack for each thread must be large enough to satisfy the stack needs of the thread’s code, plus enough additional space to hold the entire register state at the time of a task switch.

void avr_thread_stop(void)   function

This function stops the current thread. It does this by taking the current thread out of the task list and forcing a task switch.

void avr_thread_sleep(uint16_t ticks)   function

This function places the given thread to sleep for the given number of task-switcher ticks, ticks. The thread automatically awakens after the given time has elapsed.

Passing zero to this function forces a task switch and is equivalent to calling avr_thread_yield().

void avr_thread_yield(void)   inline

This function forces an explicit task-switch away from the current thread. It is implemented by calling avr_thread_sleep() with an argument of zero.

3.3  Task Switcher

The library needs the help of a timer interrupt and its interrupt service routine to provide preemptive multitasking. The following two functions are designed to be called from within an interrupt service routine tagged with the naked compiler attribute. For example, here is the interrupt service routine for TIMER2 on the Atmel ATmega128 microcontroller:

void SIG_OUTPUT_COMPARE2(void) __attribute__((naked));
void SIG_OUTPUT_COMPARE2(void)
{
    /* Global interrupt can be re-enabled here if desired. */
    /* sei(); */
    avr_thread_isr_start();
    /* Place your normal ISR code here. */
    /* ... */
    /* This must be the last function called. */
    avr_thread_isr_end();
}

Also, here is an example configuration of TIMER2 that yields approximately 1kHz preemptive task switch frequency with a 16MHz crystal:

    /* ... */
    /* Setup TIMER2 mode.  Include reset on overflow bit. */
    /* Approximately 1 kHz for a 16 MHz crystal. */
    TCCR2 = _BV(WGM21) | _BV(CS21) | _BV(CS20);
    OCR2 = 250;
    TCNT2 = 0;
    TIMSK |= _BV(OCIE2);
    /* Initialize library. */
    avr_thread_init();
    /* Enable global interrupts to start preemptive task switching */
    sei();
    /* ... */

3.3.1  Functions

void avr_thread_isr_start(void)   function

Call this function at the beginning of the interrupt service routine used to provide the preemptive multitasking. This function saves registers on the stack in preparation for a task switch. The interrupt service routine must be declared with the attribute "naked".

If you want to run your interrupt service routine code with global interrupts enabled, place a call to sei() just before this function. No other code should be placed ahead of the call to avr_thread_isr_start() since this will disturb the thread context before this function has a chance to save it.

void avr_thread_isr_end(void)   function

Call this function at the end of the interrupt service routine used to provide the preemptive multitasking. This function does not return.