# Timer as frequency/RPM counter, CapCom offset?

Question asked by de_craemer.klaas on Jun 13, 2012
Latest reply on Jun 27, 2012 by de_craemer.klaas
Hi,

I'm trying to make an RPM counter, by using Timer1 in capture-compare mode. By using slave-mode reset the timer is reset after every trigger event.

Problem is, there seems to be consistent 25% error on the time between pulses, but this number depends on the prescaler used (25% was for prescale of 4 and 5% for prescale of 20). Basically, using a very fast timer = big error on pulse interval it seems.
The question is, what can be causing this? The error results in overestimated frequency (e.g. 33 Hz measured as 41 Hz for example)

I created a sample, in which TIM2 generates a pulse-train that I measure on an oscilloscope and input into TIM1 Channel 2. This pulse train seems to be reasonably accurate frequency-wise. I print the number to the UART and calculating the number back gives the error.

---------------------------------------------------
/* MAIN.C file */
#include "stm8s.h"
#include <string.h>
#include <stdio.h>

void SerialPutChar(char c);
void SerialPutString(char *s);
void convertToText(u16 number, unsigned char text[], u8 length);

#define ARRAY_LENGTH 4
u16 timeArray[ARRAY_LENGTH];

void main(void){
u32 avg;
int i = 0;
unsigned char str[21] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

TIM1_DeInit();
/* Set TIM1 Frequency to 500 kHz (=2Mhz/4), thus 1 unit = 0.002ms = 2*10^-6 sec = 2us*/
TIM1_TimeBaseInit(4, TIM1_COUNTERMODE_UP, 30000, 0);//Max 30000*0.002 ms = 60ms , dus onderlimiet op 990 RPM

/* TIM1 channel 1 is used (PC1) with rising edge and no divider (1) nor filter (0) */
TIM1_ICInit(TIM1_CHANNEL_2, TIM1_ICPOLARITY_RISING, TIM1_ICSELECTION_DIRECTTI,  TIM1_ICPSC_DIV1, 0x00);
/* Trigger input (TRGI) to used to synchronize the counter is TI1FP1: Filtered timer input 1 (TI1FP1) */
TIM1_SelectInputTrigger(TIM1_TS_TI2FP2);
/* Reset mode - Rising edge of the selected trigger signal (TI1FP1 in this case) re-initializes the counter and
generates an update of the registers. */
TIM1_SelectSlaveMode(TIM1_SLAVEMODE_RESET);
/* Overflow interrupt ; Capture1 interrupt enabled */
TIM1_ITConfig(TIM1_IT_UPDATE | TIM1_IT_CC2, ENABLE);
/* generate an update event */
TIM1_GenerateEvent(TIM1_EVENTSOURCE_UPDATE);
/* Clear CC1 and update Flag */
TIM1_ClearFlag(TIM1_FLAG_CC2 | TIM1_FLAG_UPDATE);
//Update URS bit van TIM_CR1 so only over/underflow generates an interrupt. That we we can detect too low frequency */
/* Enable TIM1 */
TIM1_Cmd(ENABLE);

//---------------------------
TIM2_TimeBaseInit(TIM2_PRESCALER_32, 3999);
/* PWM1 Mode configuration: Channel1 */
TIM2_OC1Init(TIM2_OCMODE_PWM1, TIM2_OUTPUTSTATE_ENABLE, 2000, TIM2_OCPOLARITY_HIGH);//31.25Hz
/* TIM2 enable counter */
TIM2_Cmd(ENABLE);
//---------------------------

/* Enable general interrupts */
enableInterrupts();

GPIO_Init(GPIOC, GPIO_PIN_6 , GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_WriteHigh(GPIOC, GPIO_PIN_6);
GPIO_Init(GPIOC, GPIO_PIN_7 , GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_WriteLow(GPIOC, GPIO_PIN_7);

/* Set GPIO for LED LD1 (PD0)  */
GPIO_Init(GPIOD, 0x01 , GPIO_MODE_OUT_PP_LOW_FAST);

/* UART2 configuration
------------------------------------------------------*/
/* UART2 configured as follow:
- BaudRate = 9600 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- UART2 Clock disabled
*/
UART2_DeInit();
UART2_Init((u32)9600, UART2_WORDLENGTH_8D, UART2_STOPBITS_1, UART2_PARITY_NO, UART2_SYNCMODE_CLOCK_DISABLE, UART2_MODE_TXRX_ENABLE);

/*Main loop*/
while (1){
//Average time array
avg=0;
for (i = 0; i < 4; i++){
avg = avg + timeArray[i];
}
avg = avg/4;

if (rpm<30){
SerialPutString("RPM<30\r\n");
} else {
//sprintf(str, "%.1f", ((float)avg));
sprintf(str, "%i", timeArray[0]);
SerialPutString(str);
SerialPutString(" RPM\r\n");
}

for (i=0; i<10000; i++){
nop();
}
}
}

/*******************************************************************************
* Function Name  : SerialPutChar
* Description    : Print a character on the HyperTerminal
* Input          : - c: The character to be printed
* Output         : None
* Return         : None
*******************************************************************************/
void SerialPutChar(char c){
UART2_SendData8(c);
while ((UART2->SR & UART2_SR_TXE ) != UART2_SR_TXE );
}

/*******************************************************************************
* Function Name  : SerialPutString
* Description    : Print a string on the HyperTerminal
* Input          : - s: The string to be printed
* Output         : None
* Return         : None
*******************************************************************************/
void SerialPutString(char *s){
while (*s != '\0'){
SerialPutChar(*s);
s ++;
}
}

/* Converts an integer to decimal numbers */
void convertToText(u16 number, unsigned char text[], u8 length){
int dec = 0;

int i = length-1;
//int i = 0;
while (number > 0){
dec = number % 10; //Get rightmost decimal digit
text[i] = dec + 48; //Convert digit to ascii character
number = (number - dec)/10;
i--;
//i++;
}
//text[i] = '\0';
}

-------------------------
Interrupt code
-------------------------
#include "stm8s_it.h"

#define ARRAY_LENGTH 4
extern u16 timeArray[];
int idx = 0;

#ifdef _COSMIC_
@far @interrupt void TIM1_CAP_COM_IRQHandler(void)
#else /* _RAISONANCE_ */
void TIM1_CAP_COM_IRQHandler(void) interrupt 12
#endif /* _COSMIC_ */
{
timeArray[idx] = (TIM1_GetCapture2()); //by the way check that compiler is well handling this operation
idx++;
if (idx == ARRAY_LENGTH)
idx = 0;
TIM1_ClearFlag(TIM1_FLAG_CC2);//Clear capture-compare flag
GPIO_WriteReverse(GPIOD, GPIO_PIN_0);//Toggle LED on opto input
}

#ifdef _COSMIC_
@far @interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void)
#else /* _RAISONANCE_ */
void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void) interrupt 11
#endif /* _COSMIC_ */
{
timeArray[idx] = 65500;
idx++;
if (idx == ARRAY_LENGTH)
idx = 0;

TIM1_ClearITPendingBit(TIM1_IT_UPDATE);
}