3
votes

I want to measure the frequency of a PWM signal. To do that, I'm using STM-F401RE and its Timer_Input_Capture function.

The problem is:

  • the input signal has a quite high frequency (approx. 2MHz) and,
  • the STM-F401RE controller has only a 80MHz clock

Therefore when using an interrupt routine for counting the number of rising edge of the input signal, it misses many rising edges (depending on the frequency of the input signal). When using an Oscilloscope and toggling an I/O-pin, I saw that it can only capture all the rising edge when the frequency is lower than 400kHz.

Question is: How to overcome this problem? or is there another way to measure an input pwm signal with high frequency ?

Thanks

2
If you have a counter in your hardware, monitor its count at fixed intervals.linuxfan says Reinstate Monica

2 Answers

7
votes

You need to setup your timer as PWM input and not capture input (TIM1 for example can do it). This way, 2 channels are used (but you have only one physical connection). Basically, the first channel will give you the period and the second channel will give you the pulse. The counters are automatically reset.

The init function:

void tim_init()
{
  TIM_SlaveConfigTypeDef sSlaveConfig;
  TIM_IC_InitTypeDef sConfigIC;
  TIM_MasterConfigTypeDef sMasterConfig;

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 65535;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  HAL_TIM_IC_Init(&htim1);

  sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
  sSlaveConfig.InputTrigger = TIM_TS_TI2FP2;
  sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sSlaveConfig.TriggerPrescaler = TIM_ICPSC_DIV1;
  sSlaveConfig.TriggerFilter = 0;
  HAL_TIM_SlaveConfigSynchronization(&htim1, &sSlaveConfig);

  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
  sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_1);

  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_2);

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);

  //Enable interrupt
  HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_2);
  HAL_TIM_IC_Start(&htim1, TIM_CHANNEL_1);
}

And the interrupt handler

void tim_irq()
{
    period = HAL_TIM_ReadCapturedValue(&htim1, TIM_CHANNEL_2);
    pulse = HAL_TIM_ReadCapturedValue(&htim1, TIM_CHANNEL_1);

    //First irq to be ignored
}
0
votes

You can calculate the cycles that your code takes in your interrupt and add the extra cycles for interrupt latency (10-20 ?). Then you can see what your maximum capture frequency will be. I'll bet it will be close to 400Khz.

I think there is no way that this can be done with STM-F401RE running at 80Mhz.