unit uICP; { $NOSHADOW} // Comment this CompilerConst out, to enforce full register saving in the interrupt. Do this in the MAIN program too! interface // DO NOT FORGET to set the ICP1 Pin to INPUT in the DDR! {$IDATA} Var icp_period_buf, delta_buf : AVFilter [0..7] of Word; iprescale : Byte; iiSerPortDSR : Byte; procedure InitTimer1Int; // Initialize the Time1 for Freq./PWM counting function get_Freq : Float; // Get the current Frequency in Hz function get_PWM : Float; // Get the current PWM in % implementation Var icp_start_time, icp_stop_time, icp_period, delta, icr : Word; Interrupt Timer1CompA; // Overflow of Timer1, because no Pulse came in: so reset the Prescaling-Window begin iprescale := TCCR1B AND $07; if (iprescale < 5) then iprescale := iprescale + 1; EndIf; TCCR1B := (TCCR1B AND NOT $07) OR iprescale; end; Interrupt Timer1Capt; // This Interrupt is called depending on the frequency at the Int/Capt Input. begin icr := ICR1; // save immediately the current Timer-Count if ((TCCR1B AND $040) = 0) then // Interrupt was on falling edge icp_stop_time := icr; // Pulse has stopped: save the stop time else // Interrupt was on rising edge icp_period := icr - icp_start_time; // calculate length of the full PWM-period delta := icp_stop_time - icp_start_time; // calculate length of the previous pulse AddAVFilter(icp_period_buf,icp_period); AddAVFilter(delta_buf,delta); OCR1A := icr + 60000; // Assume the maximum timeout for the next pulse transition // if within 60.000 Timer tics no transision occurs, the // TIMER1_COMPA_vect Interrupt will fire! if (icp_period < 2000) then // 2.000 x 8 = 16.000 --> maximum jump, because // maximum factor is 8. Empirically tested, don't use // greater values... TCCR1B := (TCCR1B AND NOT $07) OR 1; // reset the scale to 1 and let the TIMER1_COMPA_vect // do the adjustment of the best (=finest) scale // since there is o prescale now, the timer runs endif; // as fast as possible, so the adjustment is fast too! icp_start_time := icr; // Start of new pulse/period: save the new start time EndIf; TCCR1B := TCCR1B XOR $040; // Reverse Bit 6 = Interrupt on rising / falling edge end; function get_Freq : Float; Var lCnt : Byte; lips, lFreq : Float; lprescale, lScaleMask : Word; begin lprescale := 1; lFreq := ProcClock; lScaleMask := Word(TCCR1B AND $07); lips := Float(GetAVFilter(icp_period_buf)); if (lScaleMask = 1) then lprescale := 1; endif; if (lScaleMask = 2) then lprescale := 8; endif; if (lScaleMask = 3) then lprescale := 64; endif; if (lScaleMask = 4) then lprescale := 256; endif; if (lScaleMask = 5) then lprescale := 1024; endif; if (lips > 0) then lFreq := Float(lprescale) * lFreq / lips; else lFreq := 0; EndIf; Return (lFreq); end; // ******************************************************* // * calculate Pulse Width in % from the Interrupt values // ******************************************************* function get_PWM : Float; Var lCnt : Byte; lPWM, lips : Float; begin lPWM := 0; lips := 0; lPWM := Float(GetAVFilter(delta_buf)); lips := Float(GetAVFilter(icp_period_buf)); if (lips > 0) Then lPWM := lPWM / lips; else lPWM := 0; EndIf; Return (lPWM); End; procedure InitTimer1Int; Var lCnt : Integer; begin TIMSK1 := TIMSK1 AND NOT $022; // DISABLE Input Capture Interrupt and Output Compare A Match Interrupt icp_start_time := 0; icp_stop_time := 0; icp_period := 0; delta := 0; PresetAVfilter(icp_period_buf,0); PresetAVfilter(delta_buf,0); TCCR1A := 0; // Timer 1: nothing set! TCCR1B := $041; // Interrupt on rising edge, set prescaler = 1 // Timer in Normal Mode, Incrementing, Timer overruns 0xFFFF to 0x0000 // Pins OC1A and OC1B disconnected OCR1A := 0; // Timer 1: Output Compare Register: nothing to set! TIMSK1 := TIMSK1 Or $022; // Input Capture Interrupt Enable and Output Compare A Match Interrupt Enable // 0x20 = ICF1 for enabling the TIMER1_CAPT_vect end; initialization finalization end.