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
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 }
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.