LoTimer.c

Go to the documentation of this file.
00001 /**
00002    \file  Robots/LoBot/irccm/LoTimer.c
00003    \brief Timer API for Robolocust's iRobot Create Command Module control
00004    program.
00005 
00006    This file defines the functions and variables used to implement the
00007    API for "generalized" one and ten millisecond timers for the iRobot
00008    Create Command Module control program used by Robolocust. This API
00009    provides a means for other parts of the control program to setup "stop
00010    clocks" and perform whatever operations they need while this stop
00011    clock ticks away. Additionally, these timers are "generalized" in the
00012    sense that client modules may register arbitrary callback functions
00013    that will be triggered each time the timer ISR kicks in.
00014 */
00015 
00016 /*
00017  ************************************************************************
00018  * The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   *
00019  * by the University of Southern California (USC) and the iLab at USC.  *
00020  * See http:  iLab.usc.edu for information about this project.          *
00021  *                                                                      *
00022  * Major portions of the iLab Neuromorphic Vision Toolkit are protected *
00023  * under the U.S. patent ``Computation of Intrinsic Perceptual Saliency *
00024  * in Visual Environments, and Applications'' by Christof Koch and      *
00025  * Laurent Itti, California Institute of Technology, 2001 (patent       *
00026  * pending; application number 09/912,225 filed July 23, 2001; see      *
00027  * http:  pair.uspto.gov/cgi-bin/final/home.pl for current status).     *
00028  ************************************************************************
00029  * This file is part of the iLab Neuromorphic Vision C++ Toolkit.       *
00030  *                                                                      *
00031  * The iLab Neuromorphic Vision C++ Toolkit is free software; you can   *
00032  * redistribute it and/or modify it under the terms of the GNU General  *
00033  * Public License as published by the Free Software Foundation; either  *
00034  * version 2 of the License, or (at your option) any later version.     *
00035  *                                                                      *
00036  * The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  *
00037  * that it will be useful, but WITHOUT ANY WARRANTY; without even the   *
00038  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      *
00039  * PURPOSE.  See the GNU General Public License for more details.       *
00040  *                                                                      *
00041  * You should have received a copy of the GNU General Public License    *
00042  * along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   *
00043  * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   *
00044  * Boston, MA 02111-1307 USA.                                           *
00045  ************************************************************************
00046 */
00047 
00048 /*
00049    Primary maintainer for this file: mviswana usc edu
00050    $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/irccm/LoTimer.c $
00051    $Id: LoTimer.c 13781 2010-08-12 17:37:02Z mviswana $
00052 */
00053 
00054 /*------------------------------ HEADERS ------------------------------*/
00055 
00056 // lobot headers
00057 #include "LoTimer.h"
00058 
00059 // AVR headers
00060 #include <avr/interrupt.h>
00061 #include <avr/io.h>
00062 
00063 /*----------------------------- CONSTANTS -----------------------------*/
00064 
00065 // Both the 1ms and 10ms timers are associated with a list of arbitrary
00066 // callback functions that facilitate asynchronous operations across the
00067 // entire low-level controller. This enum specifies the maximum number of
00068 // callbacks supported for each "generalized timer."
00069 enum {
00070    LOBOT_MAX_TIMER_CALLBACKS = 5
00071 } ;
00072 
00073 /*-------------------------- DATA STRUCTURES --------------------------*/
00074 
00075 // This structure is used to hold each generalized timer's list of
00076 // callbacks.
00077 typedef struct {
00078    int n ; // total number of callbacks in the list
00079    TimerCallback cb[LOBOT_MAX_TIMER_CALLBACKS] ; // callback list entries
00080 } cb_list ;
00081 
00082 /*------------------------------ GLOBALS ------------------------------*/
00083 
00084 // This module uses the following variable to keep track of the total
00085 // number of milliseconds that have elapsed since the 1ms timer was
00086 // setup.
00087 static volatile unsigned long g_total_ticks ;
00088 
00089 /*
00090    The stop watch implemented by this module works as follows: client
00091    modules setup a timer for some number of milliseconds and then test to
00092    see if the stop watch is running. For example:
00093 
00094            lo_setup_timer(1000) ;
00095            while (lo_timer_is_running())
00096               do_something() ;
00097 
00098    These two variables are used internally by this module to implement
00099    the above functionality.
00100 
00101    When clients setup a timer, g_stop_watch_ticks will be set to the
00102    number of milliseconds the stop watch should last and the
00103    g_stop_watch_running flag will be set to indicate that the stop watch
00104    is on.
00105 
00106    The one millisecond timer's interrupt handler will then count down
00107    g_stop_watch_ticks. When the ticks reach zero, the interrupt handler
00108    will clear the g_stop_watch_running flag to indicate that the stop
00109    watch is done.
00110 
00111    DEVNOTE: These two variables are modified by an interrupt service
00112    routine. Therefore, they *must* be declared volatile to prevent the
00113    compiler from optimizing them away.
00114 */
00115 static volatile unsigned int g_stop_watch_ticks ;
00116 static volatile char g_stop_watch_running ;
00117 
00118 // These two variables hold the callbacks for the two "generalized
00119 // timers" implemented by this module.
00120 static volatile cb_list g_timer1_callbacks  ;
00121 static volatile cb_list g_timer10_callbacks ;
00122 
00123 /*-------------------------- INITIALIZATION ---------------------------*/
00124 
00125 /*
00126    The following function sets up the 8-bit Timer0 to generate an
00127    interrupt every 1 ms (thus its designation within the low-level
00128    controller as timer1).
00129 
00130    To do this, we need to set the WGM01 (Waveform Generation Mode) bit in
00131    Timer0's Timer/Counter Control Register (TCCR0A). This will enable
00132    Clear Timer on Compare (CTC) mode so that the hardware automatically
00133    clears the timer's count register after the timer fires (which means
00134    that we don't have to do it in software).
00135 
00136    As per the ATmega168 datasheet, the WGM01 bit is bit #1. Since we
00137    don't care about the other bits in TCCR0A, we can set those to zero.
00138    Therefore, the value for TCCR0A will have to 0x02 in order to setup
00139    the timer in the mode we want.
00140 
00141    Next, we need to select a clock source for the timer. To do that we
00142    have to set the CS bits in TCCR0B. These are bits 0, 1 and 2. Again,
00143    we don't care about the other bits in this register.
00144 
00145    Since we want a timer with one millisecond resolution and the main
00146    clock signal is running at 18.432MHz, we will need a prescaler to
00147    ensure that the counter value we are counting up to can fit in the
00148    8-bit TCNT0 register.
00149 
00150    The timer's counter value for a given target frequency is computed
00151    using the following formula:
00152 
00153                             C = F/(p * f) - 1
00154 
00155    where F = the main clock frequency (18.432MHz)
00156          p = the selected prescaler
00157          f = the target frequency
00158 
00159    For the ATmega168, Timer0 supports prescaler values of 1 (i.e., no
00160    prescale, which means that the timer runs at the same speed as the
00161    core CPU), 16, 64, 256 and 1024.
00162 
00163    Since we want a 1ms timer, our target frequency, f, is 1000Hz.
00164 
00165    Thus, the possible counter values for the available prescalers are:
00166 
00167                                p        C
00168                              ----    ------
00169                                 1    18,431
00170                                16     1,151
00171                                64       287
00172                               256        71
00173                              1024        17
00174 
00175    Clearly, the first three options are unviable because we will need a
00176    16-bit counter to store those C values. Therefore, we go with one of
00177    the last two in the above table. Quite arbitrarily, we choose p = 1024
00178    and C = 17.
00179 
00180    To use a prescaler of 1024, we will have to set bits CS02 and CS00 in
00181    TCCR0B. In hex, the value we're talking about is 0x05.
00182 
00183    And, as shown above, the Output Compare Register (OCR0A) will have to
00184    be initialized with a value of 17 (or 0x11 in hexadecimal).
00185 
00186    Finally, to actually generate the desired interrupt when OCR0A reaches
00187    a value of 17, we have to setup Timer0's interrupt mask by fiddling
00188    with the right set of bits in the TIMSK0 register. The bit in question
00189    is OCIE0A (Timer/Counter0 Output Compare Match A Interrupt Enable).
00190 
00191    As the per the ATmega168 datasheet, OCIE0A is bit #1 in TIMSK0. Thus,
00192    setting TIMSK0 to 0x02 results in the timer interrupt being fired as
00193    configured by TCCR0A, TCCR0B and OCR0A.
00194 */
00195 static void timer1_init(void)
00196 {
00197    TCCR0A = 0x02 ; // CTC mode
00198    TCCR0B = 0x05 ; // prescaler = 1024
00199    OCR0A  = 0x11 ; // count from 0 to 17 for 1ms
00200    TIMSK0 = 0x02 ; // trigger interrupt when OCR0A reaches above value
00201 }
00202 
00203 /*
00204    The following function sets up the 8-bit Timer2 to generate an
00205    interrupt every 10 ms (thus its designation within this low-level
00206    control program as timer10).
00207 
00208    To setup Timer2 to fire every 10ms, we first need to set the WGM01
00209    (Waveform Generation Mode) bit in Timer2's Timer/Counter Control
00210    Register (TCCR2A). This will enable Clear Timer on Compare (CTC) mode
00211    so that the hardware automatically clears the timer's count register
00212    after the timer fires (which means that we don't have to do it in
00213    software).
00214 
00215    As per the ATmega168 datasheet, the WGM01 bit is bit #1. Since we
00216    don't care about the other bits in TCCR2A, we can set those to zero.
00217    Therefore, the value for TCCR2A will have to be 0x02 in order to setup
00218    the timer in the mode we want.
00219 
00220    Next, we need to select a clock source for the timer. To do that we
00221    have to set the CS bits in TCCR2B. These are bits 0, 1 and 2. Again,
00222    we don't care about the other bits in this register.
00223 
00224    Since we want a timer with 10 ms resolution and the main clock signal
00225    is running at 18.432MHz, we will need a prescaler to ensure that the
00226    counter value we are counting up to can fit in the 8-bit TCNT2
00227    register.
00228 
00229    The timer's counter value for a given target frequency is computed
00230    using the following formula:
00231 
00232                             C = F/(p * f) - 1
00233 
00234    where F = the main clock frequency (18.432MHz)
00235          p = the selected prescaler
00236          f = the target frequency
00237 
00238    For the ATmega168, Timer2 supports prescaler values of 1 (i.e., no
00239    prescale, which means that the timer runs at the same speed as the
00240    core CPU), 8, 32, 64, 128, 256 and 1024.
00241 
00242    Since we want a 10ms timer, our target frequency, f, is 100Hz.
00243 
00244    Thus, the possible counter values for the available prescalers are:
00245 
00246                                p       C
00247                              ----   -------
00248                                 1   184,319
00249                                32     5,759
00250                                64     2,879
00251                               128     1,439
00252                               256       719
00253                              1024       179
00254 
00255    Clearly, the last option is the only viable one because its C value is
00256    the only one that will fit in an 8-bit register.
00257 
00258    To use a prescaler of 1024, we will have to set bits CS22, CS21 and
00259    CS20 in TCCR2B. In hex, the value we're talking about is 0x07.
00260 
00261    And, as shown above, the Output Compare Register (OCR2A) will have to
00262    be initialized with a value of 179 (B3 in hexadecimal).
00263 
00264    Finally, to actually generate the desired interrupt when OCR2A reaches
00265    a value of 17, we have to setup Timer0's interrupt mask by fiddling
00266    with the right set of bits in the TIMSK2 register. The bit in question
00267    is OCIE2A (Timer/Counter2 Output Compare Match A Interrupt Enable).
00268 
00269    As the per the ATmega168 datasheet, OCIE2A is bit #1 in TIMSK2. Thus,
00270    setting TIMSK2 to 0x02 results in the timer interrupt being fired as
00271    configured by TCCR2A, TCCR2B and OCR2A.
00272 */
00273 static void timer10_init(void)
00274 {
00275    TCCR2A = 0x02 ; // CTC mode
00276    TCCR2B = 0x07 ; // prescaler = 1024
00277    OCR2A  = 0xB3 ; // count from 0 to 179 for 10ms
00278    TIMSK2 = 0x02 ; // trigger interrupt when OCR2A reaches above value
00279 }
00280 
00281 // This function initializes the two "generalized timers" supported by
00282 // this module.
00283 void lo_init_timer(void)
00284 {
00285    timer1_init() ;
00286    timer10_init() ;
00287 }
00288 
00289 // Add a 1ms timer callback
00290 void lo_add_timer1_cb(TimerCallback t)
00291 {
00292    if (g_timer1_callbacks.n < LOBOT_MAX_TIMER_CALLBACKS)
00293       g_timer1_callbacks.cb[g_timer1_callbacks.n++] = t ;
00294 }
00295 
00296 // Add a 10ms timer callback
00297 void lo_add_timer10_cb(TimerCallback t)
00298 {
00299    if (g_timer10_callbacks.n < LOBOT_MAX_TIMER_CALLBACKS)
00300       g_timer10_callbacks.cb[g_timer10_callbacks.n++] = t ;
00301 }
00302 
00303 /*---------------------- TIMER INTERRUPT HANDLERS ---------------------*/
00304 
00305 // Helper function to invoke each of the callback functions stored in the
00306 // supplied callback list.
00307 static void trigger_callbacks(volatile cb_list* callbacks)
00308 {
00309    for (int i = 0; i < callbacks->n; ++i)
00310       (*(callbacks->cb[i]))() ;
00311 }
00312 
00313 // Timer0 interrupt handler for the 1ms timer: this "generalized timer"
00314 // is responsible for maintaining the "global clock" (viz., the total
00315 // 1ms ticks since the timer was setup) and for implementing the 1ms
00316 // resolution timer delays.
00317 //
00318 // Additionally, it triggers the 1ms timer callbacks registered by client
00319 // modules.
00320 ISR(TIMER0_COMPA_vect)
00321 {
00322    ++g_total_ticks ;
00323 
00324    if (g_stop_watch_ticks)
00325       --g_stop_watch_ticks ;
00326    else
00327       g_stop_watch_running = 0 ;
00328 
00329    trigger_callbacks(&g_timer1_callbacks) ;
00330 }
00331 
00332 // Timer2 interrupt handler for the 10ms timer: this "generalized timer"
00333 // only triggers the callback functions registered by client modules; it
00334 // has no other task other than helping facilitate application wide
00335 // support for asynchronous operation in 10ms increments.
00336 ISR(TIMER2_COMPA_vect)
00337 {
00338    trigger_callbacks(&g_timer10_callbacks) ;
00339 }
00340 
00341 /*----------------------------- TIMER API -----------------------------*/
00342 
00343 // Return the current total number of milliseconds that have elapsed
00344 // since Timer0 was setup.
00345 unsigned long lo_ticks(void)
00346 {
00347    return g_total_ticks ;
00348 }
00349 
00350 // Setup a stop watch for the specified number of milliseconds
00351 void lo_setup_timer(unsigned int ms)
00352 {
00353    g_stop_watch_ticks   = ms ;
00354    g_stop_watch_running = 1 ;
00355 }
00356 
00357 // Check if a stop watch is running
00358 char lo_timer_is_running()
00359 {
00360    return g_stop_watch_running ;
00361 }
00362 
00363 // Busy wait for the specified number of milliseconds
00364 void lo_wait(unsigned int ms)
00365 {
00366    g_stop_watch_ticks   = ms ;
00367    g_stop_watch_running = 1 ;
00368    while (g_stop_watch_running)
00369       ;
00370 }
Generated on Sun May 8 08:41:30 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3