The following code combines the HardwareTimer input capture example sketch with the "change PWM frequency" example code snippet in the wiki to demonstrate how the frequency of a signal can be measured precisely by an STM32 development board, in this case a WeAct STM32F411CEU6 "Black Pill" development board. Two timers are used, Timer 2 (input capture) and Timer 4 (PWM generation).
Tested with stm32 Core release 2.0.0.
Code: Select all
/*
Input capture
This example shows how to configure HardwareTimer in inputcapture mode to measure external signal frequency.
Each time a rising edge is detected on the input pin, hardware will save counter value into CaptureCompare register.
This example has been modified to run on the STM32F411CEU6 Black Pill.
We generate a 2kHz signal on PB9 - channel 4 of Timer 4, and connect it to PA1 - channel 2 of Timer 2 to measure its
frequency.
Measured frequency (2kHz) should be displayed on Serial Monitor.
*/
/*
Note: Please verify that 'pin' used for PWM has HardwareTimer capability for your board
This is specially true for F1 serie (BluePill, ...)
*/
#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION < 0x01090000)
#error "Due to API change, this sketch is compatible with STM32_CORE_VERSION >= 0x01090000"
#endif
#define measure_frequency_pin PA1 // Timer 2 Channel 2 from Arduino_Core_STM32/variants/STM32F4xx/F411C(C-E)(U-Y)/PeripheralPins_BLACKPILL_F411CE.c
uint32_t channel;
volatile uint32_t FrequencyMeasured, LastCapture = 0, CurrentCapture, CurCap32;
uint32_t input_freq = 0;
volatile uint32_t rolloverCompareCount = 0;
HardwareTimer *MyTim;
void InputCapture_IT_callback(void)
{
CurCap32 =
CurrentCapture = MyTim->getCaptureCompare(channel);
/* frequency computation */
if (CurrentCapture > LastCapture) {
FrequencyMeasured = input_freq / (CurrentCapture - LastCapture);
}
else if (CurrentCapture <= LastCapture) {
/* 0x10000 is max overflow value */
FrequencyMeasured = input_freq / (0x10000 + CurrentCapture - LastCapture);
}
LastCapture = CurrentCapture;
rolloverCompareCount = 0;
}
/* In case of timer rollover, frequency is to low to be measured set value to 0
To reduce minimum frequency, it is possible to increase prescaler. But this is at a cost of precision. */
void Rollover_IT_callback(void)
{
rolloverCompareCount++;
if (rolloverCompareCount > 1)
{
FrequencyMeasured = 0;
}
}
void setup()
{
Serial.begin(115200);
// generate a test 2kHz square wave on PB9 PWM pin, using Timer 4 channel 4
// PB9 is Timer 4 Channel 4 from Arduino_Core_STM32/variants/STM32F4xx/F411C(C-E)(U-Y)/PeripheralPins_BLACKPILL_F411CE.c
analogWriteFrequency(2000); // default PWM frequency is 1kHz, change it to 2kHz
analogWrite(PB9, 127); // 127 means 50% duty cycle so a square wave
// Automatically retrieve TIM instance and channel associated to measure_frequency_pin
// in this case it should retrieve Timer 2 channel 2 which is associated to PA1
// This automatic algorithm is used to be compatible with all STM32 series automatically.
// Here we are just checking that it works.
TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(measure_frequency_pin), PinMap_PWM);
channel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(measure_frequency_pin), PinMap_PWM));
// Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup() function is finished.
MyTim = new HardwareTimer(Instance);
// Configure rising edge detection to measure frequency
MyTim->setMode(channel, TIMER_INPUT_CAPTURE_RISING, measure_frequency_pin);
// With a PrescalerFactor = 1, the minimum frequency value to measure is : TIM counter clock / CCR MAX
// = (SystemCoreClock) / 65535
// Example on Nucleo_L476RG with systemClock at 80MHz, the minimum frequency is around 1,2 khz
// To reduce minimum frequency, it is possible to increase prescaler. But this is at a cost of precision.
// The maximum frequency depends on processing of the interruption and thus depend on board used
// Example on Nucleo_L476RG with systemClock at 80MHz the interruption processing is around 4,5 microseconds and thus Max frequency is around 220kHz
uint32_t PrescalerFactor = 1;
MyTim->setPrescaleFactor(PrescalerFactor);
MyTim->setOverflow(0x10000); // Max Period value to have the largest possible time to detect rising edge and avoid timer rollover
MyTim->attachInterrupt(channel, InputCapture_IT_callback);
MyTim->attachInterrupt(Rollover_IT_callback);
MyTim->resume();
// Compute this scale factor only once
input_freq = MyTim->getTimerClkFreq() / MyTim->getPrescaleFactor();
}
void loop()
{
/* Print frequency measured on Serial monitor every seconds */
Serial.print("Timer channel = ");
Serial.println(channel);
Serial.print("Timer Clock Frequency = ");
Serial.println(MyTim->getTimerClkFreq());
Serial.println((String)"Frequency = " + FrequencyMeasured);
delay(1000);
}
UPDATE: I have posted an extended (improved?) version of this example below.