Closed exothink closed 3 years ago
HI @exothink, You didn't specified which version of stm32duino core you are using ... whatever, can you check with the 2.0 release which just come out ? (there is much more capabilities to access pin alternate/remap functions in core 2.0) Warning for this new version, you need to add new json in Arduino IDE https://github.com/stm32duino/BoardManagerFiles/raw/master/package_stmicroelectronics_index.json (see https://github.com/stm32duino/wiki/wiki/Getting-Started)
Can you also specify which
Also can you share your sketch (a minimal one with only the 2 hardware timers), thanks
Hello Alexandre,
Attached is the code I’m using. It works using PB5/PB6. Additional info has been added in the comment section.
Regards,
/*
Frequency and dutycycle measurement
This example shows how to configure HardwareTimer to measure external signal frequency and dutycycle.
The input pin will be connected to 2 channel of the timer, one for rising edge the other for falling edge.
Each time a rising edge is detected on the input pin, hardware will save counter value into one of the CaptureCompare register.
Each time a falling edge is detected on the input pin, hardware will save counter value into the other CaptureCompare register.
External signal (signal generator for example) should be connected to D2
.
*/
/* PLATFORM: ST STM32 (11.0.0) > STM32F103CB (20k RAM. 128k Flash) HARDWARE: STM32F103CBT6 72MHz, 20KB RAM, 128KB Flash DEBUG: Current (blackmagic) External (blackmagic, cmsis-dap, jlink, stlink) PACKAGES:
// #define mphPin PA8 // t1c1 old proto brd ---runs // #define rpmPin PB5 // t3c2
// #define mphPin PB6 // TIM4_CH1 --freezes // #define rpmPin PA6 // TIM3_CH2
// #define mphPin PB7 // t4c2 --freezes // #define rpmPin PA6 // t3c1
// #define mphPin PB7 // t4c2 --freezes // #define rpmPin PA0 // t2c1
// #define mphPin PA6 // t3c1
// #define mphPin PA0 // t2c1 ---runs // #define rpmPin PA6 // t3c1
// #define mphPin PA6 // t3c1 ---runs // #define rpmPin PB5 // t2c2
*/
//=========================================
//=========================================
uint32_t channelRising, channelFalling; uint32_t mphchannelRising, mphchannelFalling; volatile uint32_t FrequencyMeasured, DutycycleMeasured, LastPeriodCapture = 0, CurrentCapture, HighStateMeasured; volatile uint32_t mphFrequencyMeasured, mphDutycycleMeasured, mphLastPeriodCapture = 0, mphCurrentCapture, mphHighStateMeasured;
uint32_t input_freq = 0; uint32_t mphinput_freq = 0; volatile uint32_t rolloverCompareCount = 0; volatile uint32_t mphrolloverCompareCount = 0; HardwareTimer MyTim; HardwareTimer mphMyTim;
/** @brief Input capture interrupt callback : Compute frequency and dutycycle of input signal
/ void TIMINPUT_Capture_Rising_IT_callback(void) { CurrentCapture = MyTim->getCaptureCompare(channelRising); / frequency computation / if (CurrentCapture > LastPeriodCapture) { FrequencyMeasured = input_freq / (CurrentCapture - LastPeriodCapture); DutycycleMeasured = (HighStateMeasured 100) / (CurrentCapture - LastPeriodCapture); } else if (CurrentCapture <= LastPeriodCapture) { / 0x1000 is max overflow value / FrequencyMeasured = input_freq / (0x10000 + CurrentCapture - LastPeriodCapture); DutycycleMeasured = (HighStateMeasured * 100) / (0x10000 + CurrentCapture - LastPeriodCapture); }
LastPeriodCapture = CurrentCapture; rolloverCompareCount = 0; }
void mphTIMINPUT_Capture_Rising_IT_callback(void) { mphCurrentCapture = mphMyTim->getCaptureCompare(channelRising); / frequency computation / if (mphCurrentCapture > mphLastPeriodCapture) { mphFrequencyMeasured = mphinput_freq / (mphCurrentCapture - mphLastPeriodCapture); mphDutycycleMeasured = (mphHighStateMeasured 100) / (mphCurrentCapture - mphLastPeriodCapture); } else if (mphCurrentCapture <= mphLastPeriodCapture) { / 0x1000 is max overflow value / mphFrequencyMeasured = mphinput_freq / (0x10000 + mphCurrentCapture - mphLastPeriodCapture); mphDutycycleMeasured = (mphHighStateMeasured 100) / (0x10000 + mphCurrentCapture - mphLastPeriodCapture); }
mphLastPeriodCapture = mphCurrentCapture; mphrolloverCompareCount = 0; }
/ In case of timer rollover, frequency is to low to be measured set values 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; DutycycleMeasured = 0; } }
void mphRollover_IT_callback(void) { mphrolloverCompareCount++;
if (mphrolloverCompareCount > 1) { mphFrequencyMeasured = 0; mphDutycycleMeasured = 0; } }
/** @brief Input capture interrupt callback : Compute frequency and dutycycle of input signal
/ void TIMINPUT_Capture_Falling_IT_callback(void) { / prepare DutyCycle computation */ CurrentCapture = MyTim->getCaptureCompare(channelFalling);
if (CurrentCapture > LastPeriodCapture) { HighStateMeasured = CurrentCapture - LastPeriodCapture; } else if (CurrentCapture <= LastPeriodCapture) { / 0x1000 is max overflow value / HighStateMeasured = 0x10000 + CurrentCapture - LastPeriodCapture; } }
void mphTIMINPUT_Capture_Falling_IT_callback(void) { / prepare DutyCycle computation / mphCurrentCapture = mphMyTim->getCaptureCompare(channelFalling);
if (mphCurrentCapture > mphLastPeriodCapture) { mphHighStateMeasured = mphCurrentCapture - mphLastPeriodCapture; } else if (CurrentCapture <= LastPeriodCapture) { / 0x1000 is max overflow value / mphHighStateMeasured = 0x10000 + CurrentCapture - LastPeriodCapture; } } void setup() { Serial.begin(921600);
// Automatically retrieve TIM instance and channelRising associated to pin // This is used to be compatible with all STM32 series automatically. TIM_TypeDef Instance = (TIM_TypeDef )pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM); channelRising = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM));
// channelRisings come by pair for TIMER_INPUT_FREQ_DUTY_MEASUREMENT mode: // channelRising1 is associated to channelFalling and channelRising3 is associated with channelRising4 switch (channelRising) { case 1: channelFalling = 2; break; case 2: channelFalling = 1; break; case 3: channelFalling = 4; break; case 4: channelFalling = 3; break; }
// 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(channelRising, TIMER_INPUT_FREQ_DUTY_MEASUREMENT, 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 both interruptions and thus depend on board used // Example on Nucleo_L476RG with systemClock at 80MHz the interruptions processing is around 10 microseconds and thus Max frequency is around 100kHz uint32_t PrescalerFactor = 128; // good for 900-7200rpm 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(channelRising, TIMINPUT_Capture_Rising_IT_callback); MyTim->attachInterrupt(channelFalling, TIMINPUT_Capture_Falling_IT_callback); MyTim->attachInterrupt(Rollover_IT_callback);
MyTim->resume();
// Compute this scale factor only once input_freq = MyTim->getTimerClkFreq() / MyTim->getPrescaleFactor();
//=======================================================================================
// Automatically retrieve TIM instance and channelRising associated to pin // This is used to be compatible with all STM32 series automatically. TIM_TypeDef mphInstance = (TIM_TypeDef )pinmap_peripheral(digitalPinToPinName(mphpin), PinMap_PWM); mphchannelRising = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(mphpin), PinMap_PWM));
// channelRisings come by pair for TIMER_INPUT_FREQ_DUTY_MEASUREMENT mode: // channelRising1 is associated to channelFalling and channelRising3 is associated with channelRising4 switch (mphchannelRising) { case 1: mphchannelFalling = 2; break; case 2: mphchannelFalling = 1; break; case 3: mphchannelFalling = 4; break; case 4: mphchannelFalling = 3; break; }
// Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup() function is finished. mphMyTim = new HardwareTimer(mphInstance);
// Configure rising edge detection to measure frequency mphMyTim->setMode(mphchannelRising, TIMER_INPUT_FREQ_DUTY_MEASUREMENT, mphpin);
// 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 both interruptions and thus depend on board used // Example on Nucleo_L476RG with systemClock at 80MHz the interruptions processing is around 10 microseconds and thus Max frequency is around 100kHz uint32_t mphPrescalerFactor = 256; // 225 good for 1 mph mphMyTim->setPrescaleFactor(mphPrescalerFactor); mphMyTim->setOverflow(0x10000); // Max Period value to have the largest possible time to detect rising edge and avoid timer rollover mphMyTim->attachInterrupt(mphchannelRising, mphTIMINPUT_Capture_Rising_IT_callback); mphMyTim->attachInterrupt(mphchannelFalling, mphTIMINPUT_Capture_Falling_IT_callback); mphMyTim->attachInterrupt(mphRollover_IT_callback);
mphMyTim->resume();
// Compute this scale factor only once mphinput_freq = mphMyTim->getTimerClkFreq() / mphMyTim->getPrescaleFactor(); Serial.println("042221"); }
void loop()
{
/ Print frequency and dutycycle measured on Serial monitor every seconds /
Serial.printf("r \t%d \t%d\n", FrequencyMeasured 900 / 30, mphFrequencyMeasured 64 / 183);
// Serial.println(" % = " + DutycycleMeasured);
// Serial.print((String)"mphFreq: " + mphFrequencyMeasured); // Serial.println((String)" % = " + mphDutycycleMeasured); delay(100); }
Hi @exothink ,
I tested you sketch and here are my findings:
1) in mphTIMINPUT_Capture_Rising_IT_callback()
there is use of channelRising
, but it should bemphchannelRising
The same for mphTIMINPUT_Capture_Falling_IT_callback()
with channelFalling
2) I didn't check all combinations you provided, but I tested on my bluePill F103C8T6:
#define mphpin PB6
#define pin PA6
I did not reproduce you frozen behavior, but I suspect the problem comes from definition of PinMap_PWM[] in PeripheralPins.c :
...
#ifndef ARDUINO_BLUEPILL_F103C6
{PB_6, TIM4, STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, AFIO_NONE, 1, 0)}, // TIM4_CH1
{PB_7, TIM4, STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, AFIO_NONE, 2, 0)}, // TIM4_CH2
{PB_8, TIM4, STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, AFIO_NONE, 3, 0)}, // TIM4_CH3
{PB_9, TIM4, STM_PIN_DATA_EXT(STM_MODE_AF_PP, GPIO_PULLUP, AFIO_NONE, 4, 0)}, // TIM4_CH4
#endif
in case ARDUINO_BLUEPILL_F103C6 is defined the pins above cannot be used for timer because timer4 is not supported. But it would mean that you selected BluePill F103C6 (or generic F103C6). Can you please double check your board selection ?
3) There is a typo in your comments:
// #define mphPin PB4 // t3c1 ---runs not working
// #define rpmPin PB5 // t2c2 working
In fact PB5 is TIM3CH2.
And this combination is not possible because it is the same timer TIM3 and because channel2 is already mphchannelFalling
, so it cannot also be channelRising
4) with combination:
// #define mphPin PA0 // t2c1 ---runs
// #define rpmPin PA6 // t3c1
It is working perfectly on my side, I tested with both 500 Hz and 3000Hz input frequency generator. Make sure you have GND connected from your input frequencies system to the bluepill. Verify your input signal (oscillo or Logical analyser), or better use a PWM signal generator (another board could do the job by changing frequency and duty cycle of example https://github.com/stm32duino/STM32Examples/blob/master/examples/Peripherals/HardwareTimer/All-in-one_setPWM/All-in-one_setPWM.ino)
Hi @exothink any feedback based on @ABOSTM one ? Thank you in advance.
Closed as no feedback
Using the freq/duty cycle example I duplicated all of the code and prefixed all of the var/function names in the copy to create two instances. The only difference besides the prefixed names is the pin names. At first the code froze in 'Setup'. I found certain timer/channel combinations simply froze. After eliminating the offending combinations I found that the results were all zeroes for freq and duty cycle. Then I commented out the second instance and I got the expected results. Then I uncommented that code and commented out the other instance and that one executed correctly!
Finally, I discovered a combination of pins, PB5 and PB6, that allow two instances to operate correctly. I'm using the stm32f103 blue pill boards. The goal is to measure the RPM of two separate shafts using the GP timers two through four. Single pin inputs.