AnsweredAssumed Answered

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 */
  TIM1_UpdateRequestConfig(TIM1_UPDATESOURCE_REGULAR);
  /* 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_OC1PreloadConfig(ENABLE);
    TIM2_ARRPreloadConfig(ENABLE);
    /* 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
    - Receive and transmit enabled
    - 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);
}

Outcomes