Main Page | File List | Globals

icp.c

Go to the documentation of this file.
00001 // This file has been prepared for Doxygen automatic documentation generation.
00023 #if  __GCC__
00024 #include <avr/io.h>
00025 #include <avr/signal.h>
00026 #else   /* assume IAR */
00027 #include <stdio.h>
00028 #include <inavr.h>
00029 #define ENABLE_BIT_DEFINITIONS
00030 #include <iom64.h>
00031 #endif
00032 #include "icp.h"
00033 
00034 /*
00035  * Definitions for the ICP pin; for this example we use timer 1
00036  */
00037 #define ICP_PIN         PIND                    /* ICP1 GPIO value      */
00038 #define ICP_PORT        PORTD                   /* ICP1 GPIO port       */
00039 #define ICP_DDR         DDRD                    /* ICP1 GPIO DDR        */
00040 #define ICP_BIT         PD4                             /* ICP1 GPIO pin        */
00041 
00042 /*
00043  * Definitions for ICP timer (1) setup.
00044  */
00045 #define ICP_OCR         OCR1A                   /* ICP1 Output Compare register         */
00046 #define ICP_OC_IE       OCIE1A                  /* ICP1 timer Output Compare enable */
00047 #define ICP_OC_IF       OCF1A                   /* ICP1 timer Output Compare flag       */
00048 #define ICP_IE          TICIE1                  /* ICP1 interrupt enable                        */
00049 #define ICP_IF          ICF1                    /* ICP1 interrupt flag                          */
00050 #define ICP_CTL_A       TCCR1A                  /* ICP1 timer control                           */
00051 #define ICP_CTL         TCCR1B                  /* ICP1 interrupt control                       */
00052 #define ICP_SENSE       ICES1                   /* ICP1 interrupt sense (rising/falling) */
00053 #define ICP_PRESCALE ((0 << CS12) | (0 << CS11) | (1 << CS10))  /* prescale /1 */
00054 
00055 #define ICP_START_SENSE (1 << ICP_SENSE)        /* start with rising edge       */
00056 
00065 typedef unsigned int icp_timer_t;                       /* same as TCNT1                */
00066 icp_timer_t icp_start_time, icp_stop_time;
00067 icp_timer_t icp_period;
00068 
00076 icp_sample_t icp_rx_q[ICP_RX_QSIZE];
00077 
00088 unsigned char icp_rx_tail;              /* icp_rx_q insertion index */
00089 #if     !ICP_ANALOG     /* ICP_DIGITAL */
00090 unsigned char icp_rx_head;              /* icp_rx_q retrieval index */
00091 #endif
00092 
00093 #if     ICP_ANALOG
00094 
00102 icp_total_t icp_total;
00103 #endif  /* ICP_ANALOG */
00104 
00117 #if __GCC__
00118 __inline__
00119 #else   /* assume IAR */
00120 #pragma inline      /* for next function */
00121 #endif
00122 static icp_sample_t icp_duty_compute(icp_timer_t pulsewidth, icp_timer_t period)
00123 {
00124         icp_sample_t r, mask;
00125 
00126         mask = ICP_SCALE >> 1;
00127         r = 0;
00128         do
00129         {
00130                 period >>= 1;
00131                 if (pulsewidth >= period)
00132                 {
00133                         r |= mask;
00134                         pulsewidth -= period;
00135                 }
00136                 mask >>= 1;
00137         } while (pulsewidth != 0 && mask != 0);
00138         return(r);
00139 }
00140 
00146 #if __GCC__
00147 __inline__
00148 #else   /* assume IAR */
00149 #pragma inline      /* for next function */
00150 #endif
00151 static void icp_enq(icp_sample_t sample)
00152 {
00153         unsigned char t;
00154 
00155         t = icp_rx_tail;
00156 #if     ICP_ANALOG
00157         icp_total += sample - icp_rx_q[t];
00158 #endif
00159         icp_rx_q[t] = sample;
00160         if (++t >= ICP_RX_QSIZE)
00161                 t = 0;
00162 #if     !ICP_ANALOG
00163         if (t != icp_rx_head)           /* digital: Check for Rx overrun */
00164 #endif
00165                 icp_rx_tail = t;
00166         return;
00167 }
00168 
00178 #if __GCC__
00179 SIGNAL(SIG_OUTPUT_COMPARE1A)
00180 #else   /* assume IAR */
00181 #pragma vector=TIMER1_COMPA_vect
00182 __interrupt void TIMER1_COMPA(void)
00183 #endif
00184 {
00185         icp_sample_t sample;
00186 
00187         ICP_OCR += icp_period;          /* slide timeout window forward */
00188         sample = 0;                                     /* assume 0%    */
00189         if ((ICP_CTL & (1 << ICP_SENSE)) != ICP_START_SENSE)
00190                 sample = ICP_SCALE - 1; /* 100% */
00191 
00192         icp_enq(sample);
00193 
00194         return;
00195 }
00196 
00202 #if __GCC__
00203 SIGNAL(SIG_INPUT_CAPTURE1)
00204 #else   /* assume IAR */
00205 #pragma vector=TIMER1_CAPT_vect
00206 __interrupt void TIMER1_CAPT(void)
00207 #endif
00208 {
00209         icp_timer_t icr, delta;
00210         unsigned char tccr1b;
00211 
00212         /*
00213          * Capture the ICR and then reverse the sense of the capture.
00214          * These must be done in this order, since as soon as the
00215          * sense is reversed it is possible for ICR to be updated again.
00216          */
00217         icr = ICR1;                                                     /* capture timestamp    */
00218 
00219         do
00220         {
00221                 tccr1b = ICP_CTL;
00222                 ICP_CTL = tccr1b ^ (1 << ICP_SENSE);            /* reverse sense                */
00223         
00224                 /*
00225                  * If we were waiting for a start edge, then this is the
00226                  * end/beginning of a period.
00227                  */
00228                 if ((tccr1b & (1 << ICP_SENSE)) == ICP_START_SENSE)
00229                 {
00230                         /*
00231                          * Beginning of pulse: Compute length of preceding period,
00232                          * and thence the duty cycle of the preceding pulse.
00233                          */
00234                         icp_period = icr - icp_start_time;      /* Length of previous period */
00235                         delta = icp_stop_time - icp_start_time; /* Length of previous pulse */
00236                         icp_start_time = icr;                           /* Start of new pulse/period */
00237 
00238                         /*
00239                          * Update the timeout based on the new period. (The new period
00240                          * is probably the same as the old, give or take clock drift.)
00241                          * We add 1 to make fairly sure that, in case of competition,
00242                          * the PWM edge takes precedence over the timeout.
00243                          */
00244                         ICP_OCR = icr + icp_period + 1;         /* Move timeout window          */
00245                         TIFR = (1 << ICP_OC_IF);                        /* Clear in case of race        */
00246 
00247                         /*
00248                          * Compute the duty cycle, and store the new reading.
00249                          */
00250                         icp_enq(icp_duty_compute(delta,icp_period));
00251         
00252                         /*
00253                          * Check for a race condition where a (very) short pulse
00254                          * ended before we could reverse the sense above.
00255                          * If the ICP pin is still high (as expected) OR the IF is
00256                          * set (the falling edge has happened, but we caught it),
00257                          * then we won the race, so we're done for now.
00258                          */
00259                         if ((ICP_PIN & (1 << ICP_BIT)) || (TIFR & (1 << ICP_IF)))
00260                                 break;
00261                 }
00262                 else
00263                 {
00264                         /*
00265                          * Falling edge detected, so this is the end of the pulse.
00266                          * The time is simply recorded here; the final computation
00267                          * will take place at the beginning of the next pulse.
00268                          */
00269                         icp_stop_time = icr;            /* Capture falling-edge time */
00270 
00271                         /*
00272                          * If the ICP pin is still low (as expected) OR the IF is
00273                          * set (the transition was caught anyway) we won the race,
00274                          * so we're done for now.
00275                          */
00276                         if ((!(ICP_PIN & (1 << ICP_BIT))) || (TIFR & (1 << ICP_IF)))
00277                                 break;
00278                 }
00279                 /*
00280                  * If we got here, we lost the race with a very short/long pulse.
00281                  * We now loop, pretending (as it were) that we caught the transition.
00282                  * The Same ICR value is used, so the effect is that we declare
00283                  * the duty cycle to be 0% or 100% as appropriate.
00284                  */
00285         } while (1);
00286 
00287         return;
00288 }
00289 
00297 icp_sample_t
00298 icp_rx(void)
00299 {
00300         icp_sample_t r;
00301 
00302 #if     ICP_ANALOG
00303         r = icp_total / ICP_RX_QSIZE;           /* moving average of last QSIZE samples */
00304 #else   /* ICP_DIGITAL */
00305         unsigned char h;
00306 
00307         h = icp_rx_head;
00308         if (h == icp_rx_tail)                           /* if head == tail, queue is empty */
00309                 r = (icp_sample_t)-1;
00310         else
00311         {
00312                 r = icp_rx_q[h];                                /* fetch next entry                             */
00313                 if (++h >= ICP_RX_QSIZE)                /* increment head, modulo QSIZE */
00314                         h = 0;
00315                 icp_rx_head = h;
00316         }
00317 #endif  /* ICP_DIGITAL */
00318 
00319         return(r);
00320 }
00321 
00327 void
00328 icp_init(void)
00329 {
00330         /*
00331          * Nothing interesting to set in TCCR1A
00332          */
00333         ICP_CTL_A = 0;
00334 
00335         /*
00336          * Setting the OCR (timeout) to 0 allows the full TCNT range for
00337          * the initial period.
00338          */
00339         ICP_OCR = 0;
00340 
00341         /*
00342          * Set the interrupt sense and the prescaler
00343          */
00344         ICP_CTL = ICP_START_SENSE | ICP_PRESCALE;
00345 
00346         /*
00347          *      Enable both the Input Capture and the Output Capture interrupts.
00348          *      The latter is used for timeout (0% and 100%) checking.
00349          */
00350         TIMSK   |= (1 << ICP_IE) | (1 << ICP_OC_IE);
00351 
00352         return;
00353 }

Generated on Wed Nov 2 14:20:47 2005 for AVR135: Using Timer Capture to Measure PWM Duty Cycle by  doxygen 1.4.4