Electrical – STM32 Input capture PWM frequency measurement problem using HAL

programmingpwmstm32timer

I'm trying to measure time passed between two external interrupts generated by rising edges of a PWM signal (generated using a IC 555 timer). Point is to determine the frequency of the signal. I'm using GPT (General purpouse timer) in input capture mode. The callback routine is mentioned in the code below.

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
 if (htim->Instance==TIM2)
  {
  input_capture= __HAL_TIM_GET_COMPARE(&htim2, TIM_CHANNEL_1);    //read TIM2 channel 1 capture value
  frequency=input_capture-input_capture_prev;
  input_capture_prev=input_capture;
  FLAG++;
  }
}

Every time an external interrupt happens i take the timer time and subtract it with the time when a previous interrupt happened. Now i want to make a buffer and take 20 samples which i'll divide with the length of a buffer to get a precise reading. Basicly i want to make a function which starts the timer in interrupt mode takes 20 samples divides them by 20 and returns the precise value and after that disables the timer. Here is the function code.

uint32_t sampleCapacitance(void)
{
    uint32_t sum=0;
    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
    FLAG=0;
    while(FLAG<20)
    {
        buffer[FLAG]=frequency;
        sum+=buffer[FLAG];
    }
    HAL_TIM_IC_Stop_IT(&htim2, TIM_CHANNEL_1);
    return sum/20;
}

All the variables used that aren't locally defined are global volatile variables. I'm calling this function every 1000ms, the PWM signal period has variable length (few ms to 100 ms approximatly). System clock frequency is 48MHz, and the timer prescaler value is 47999+1 which gives a time base of 1000 Hz. For some reason this doesn't work properly.(buffer values are mostly 0, except on 20th and 0th index) I'd like to know if there is a smarter way to do this and make it work. I'm really a newbie in MCU programming, especialy with ARM Cortex architecture so any help would be appreciated. Thanks in advance.

Best Answer

What you can do is to create a queue on the interrupt to store the period. Because the sampling frequency should be at least double the PWM frequency ,you have to handle the period asynchronously (because the main loop may be slow to handle these frequencies) .I had the same issue quite some time ago but instead of a PWM I had a saw tooth with variable frequency so I was just counting saw tooth edges. So here is the code for reference:

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef * htim)
{
    static uint32_t prev1 = 0;

    if (htim->Instance == TIM1 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_4 )
    {
        auto now = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_4);
        auto diff =( 65536 + now - prev1)%65536;  
        prev1 = now;
        single_producer_single_consumer_queue.push(diff);
    }
}

Notice that because my clock wasen't fast enough to count some periods I have these normalizer for the overflows which I should handle somehow (for example compare the values of the queue for immidiate huge changes from 65000 clock periods to 1 clock period)

( 65536 + now - prev1)%65536

Also notice that in capture mode HAL_TIM_ReadCapturedValue seems more reasonable and more "high level" than __HAL_TIM_GET_COMPARE