Timeout Driver

Function scheduler and stopwatch.

Revision History

  • v0.0.0.1 Initial Commit

The Timeout driver has two modes of operation:
  • Scheduler mode

  • Stopwatch mode

Scheduler mode

The Scheduler mode allows timer tasks to be scheduled a specified number of timer ticks from now. A timer task is a piece of code (i.e. a function) executed at a specific time by the timer after the task has been added to the timers task queue. The execution delay or period is set in ticks, where one tick is defined as a configurable number of clock cycles in the hardware timer implementing the Timeout driver. Changing the number of clock cycles in a tick (by changing the timer prescaler) changes execution delays for all tasks in the queue.

Stopwatch mode

The Stopwatch mode allows the program to start the stopwatch, do some operation, then stop the stopwatch and measure the duration of the operation.

Functional Description

The Timeout Driver uses structures and linked-list implementations to perform functionalities such as creating, adding, and deleting timers. The term "timer" that we will be referring to in this documentation is represented by a structure timer_struct_t. Timer tasks can be scheduled for execution at a specified time by adding them to a scheduler queue. Tasks are added to the scheduler queue using the <component_name>_timeout_create()-function. This places the timer task, or more precisely, it's timer struct at the correct place in a sorted linked list. The list is sorted by the task's scheduled time for execution. The task to execute first is at the head of the list. The linked list will have as may elements as there are tasks scheduled for execution.

Once the specified number of ticks has passed, the task is scheduled for execution. When this happens, the timer struct is removed from the linked list, and the task's callback function and it's payload is placed in another queue; the callback queue. Note that the callback function is not called automatically, the user software must execute the callbacks in the callback queue using <component_name>_timeout_call_next_callback(). If no task has been scheduled for execution, this function returns immediately, so there is no need for any polling. Using the <component_name>_timeout_call_next_callback() function instead of executing the callback immediately allows the user code to check for pending callbacks and executing them at appropriate points in time, so the execution won't disturb any timing-critical part of the application.

The task's callback function returns a value of type absolutetime_t. If this value is zero, the task will not be rescheduled automatically. If this return value is non-null, the task will be automatically rescheduled for execution after the number of ticks specified in the return value.

The stopwatch functionality is implemented using the same linked list as the scheduler mode. Multiple stopwatches may be active at the same time. A variable of type timer_struct_t is used to hold housekeeping information for each stopwatch. The linked list may hold timer structs of both scheduler and stopwatch functionality at any time. A stopwatch cannot count longer than (timer_maximum range)/2. After this point the stopwatch will automatically stop.

Hardware Dependencies

The Timeout driver needs some sort of hardware that is able to measure a time interval. In MCUs, this is typically implemented using some sort of timer/counter. Different MCUs have timers with different names and functionalities, such as TCA, TCB, tim8, tim16pwm etc. When the user has selected a device and added the Timer driver, the Driver field in the Component Settings pane in START will let the user select which timer driver to use, select Drivers:Timeout: to use the Timeout driver. Thereafter, select which of the available hardware instances to use for implementing the Timeout functionality in the Instance dropdown box.

The Configuration Pane in START will display options that are dependent on the hardware used to implement the Timeout driver. For example, an option may allow changing the clock or prescaling used to drive the underlying timer hardware.

Software Dependencies

Many Timeout implementations use the interrupt functionality of the underlying hardware timer. Make sure that global interrupts are enabled (using sei()) and that the Interrupt Controller, if present, is configured so that the timer interrupt is serviced correctly.

Code example

          #include <atmel_start.h>
          volatile uint8_t a = 0;
          volatile uint8_t b = 0;
          volatile uint16_t cycles;
          absolutetime_t foo(void * payload){
              uint8_t* p = (uint8_t *) payload;
              a = *p;
              return 10000; // Reschedule, execute after 10000 ticks
          }
          absolutetime_t bar(void * payload){
              uint8_t* p = (uint8_t *) payload;
              b = *p;
              return 0; // Do not reschedule
          }
          int main(void)
          {
              uint8_t one = 1;
              // Initialize the two first elements in the struct, the 
              // remaining elements are initialized by TIMER_0_timeout_add()
              timer_struct_t foo_handle = {foo, (void*)&one};
              timer_struct_t bar_handle = {bar, (void*)&one};
                  
              /* Initializes MCU, drivers and middleware */
              atmel_start_init();
              sei();
              
              // Let's test scheduler mode
              // Schedule foo() to be called in 10000 cycles
              TIMER_0_timeout_create(&foo_handle, 10000);  
              // Schedule bar() to be called in 200000 cycles
              TIMER_0_timeout_create(&bar_handle, 200000);  
              // Loop waiting for foo() to execute and increment a
              while (a < 3)
              {
                  // Returns immediately if no callback is ready to execute
                  TIMER_0_timeout_call_next_callback(); 
              }
              
              // bar() hasn't timed out yet, let's delete it from 
              // the schedule queue
              TIMER_0_timeout_delete(&bar_handle); 
              // Let's test stopwatch mode, measuring the number of 
              // cycles needed to execute a small loop
              TIMER_0_timeout_start_timer(&foo_handle);
              for(a=0; a<200; a++);
              cycles = TIMER_0_timeout_stop_timer(&foo_handle);
              
              while (1);
          }