Now i want to use pin PA0, timer2 channel1, for input capture. Are there any examples?
With Timer2.setMode(TIMER_CH1,mode) function, what mode must i use? What else must i set?
I have a hard time finding documentation or header files for arduino-stm32 environment. Where should i look?
The prototype functions for timerx are here
I scanned our site and could not find any hits, but on the old Leaflabs forum, I found 2 discussions that may assist you:
http://forums.leaflabs.com/topic.php?id=1291
http://forums.leaflabs.com/topic.php?id=737&page=2
Good luck. If you get it working, please consider posting an example … the Snippets sub-forum is a good place.
Ray
/* TIMER_INPUT_CAPTURE, TODO: In this mode, the timer can measure the pulse lengths of input signals */
TODO means its not implemented ![]()
#define RX1 PA0 //t2c1
void init_rx()
{
pinMode(RX1, INPUT);
Timer2.setPrescaleFactor(72);
TIMER2->regs.gen->CCMR1 = 0b010000001;
TIMER2->regs.gen->CCMR2 = 0b0;
TIMER2->regs.gen->CCER = 0b0001;
Timer2.attachCompare1Interrupt(ppm_input);
}
uint16_t last;
uint8_t chan = 0;
boolean newrc = false;
void ppm_input(void)
{
uint16_t diff,act;
act = TIMER2->regs.gen->CCR1;
......
And thanks for posting the sample code. We also have a ‘snippets’ section that I generally recommend to members for such things, it is just another way to get visibility and socialize the solution.
Ray
Do you think it would be possible to use your code in the “core” where it currently has TO DO… ?
It would be great if we could.
Input capture is quite complex. There are parameters for filter and prescaler, capture units can be cross-linked, lots of complicated goodies.
Concentrating on the basic function, no prescaler, no filter, just parameter for rising or faling edge schould be doable. I did search an NOT find where timer.c is. The file structure is quite hard to understand.
There also tons of sub-tasks we wished, such as putting much common code between platforms together, but again those are huge too.
Refactoring has always been difficult on any projects, especially when “time is the missing ingredient” …
There also tons of sub-tasks we wished, such as putting much common code between platforms together, but again those are huge too.
Refactoring has always been difficult on any projects, especially when “time is the missing ingredient” …
timer.c in STM32F1\system\libmaple\include\libmaple timer_set_mode()
case TIMER_INPUT_CAPTURE:
input_capture_mode(dev, channel);
break;
In the mean time I’m going to try to add the code you posted, but I don’t think its complete
Edit
I looked at the code for
attachCompare1Interrupt(ppm_input);
and this function is deprecated.
HardwareTimer.h reads
/** @brief Deprecated; use attachInterrupt(TIMER_CH1, handler) instead. */
void attachCompare1Interrupt(voidFuncPtr handler) {
attachInterrupt(TIMER_CH1, handler);
}
uint16_t act = TIMER2->regs.gen->CCR1;
<…>
As in all big projects, try to get there with small steps whenever you have some time ^^’.
<…>
The more I look at the HALMX core, the more I appreciate what leaflabs managed to do with libmaple.
Libmaple now looks nimble compared with the vast array of files that are needed to support multiple boards in the HALMX.
Libmaple’s USB Serial seems to take 8k but STMs version appears to take 20k ( of flash ).
But there is nothing to stop us improving, adding to, bug fixing and tidying up libmaple, while at the same time, people can work on HALMX if they want to.
Complaining about libmaple not using the CMSIS etc, seems to be a common sport, but I see hardly anyone doing anything to build anything better (@sheepdoll and @vassilis accepted)
<…>
The more I look at the HALMX core, the more I appreciate what leaflabs managed to do with libmaple. Libmaple now looks nimble compared with the vast array of files that are needed to support multiple boards in the HALMX. Libmaple’s USB Serial seems to take 8k but STMs version appears to take 20k ( of flash ). But there is nothing to stop us improving, adding to, bug fixing and tidying up libmaple, while at the same time, people can work on HALMX if they want to. Complaining about libmaple not using the CMSIS etc, seems to be a common sport, but I see hardly anyone doing anything to build anything better (@sheepdoll and @vassilis accepted)
i want to use Input capture method , instead of AttachInterrupt i read that is faster MCU/2 speed.
I think I can use this in my AC dimmer project
However, there is one thing I don’t understand.
Is only one of the GPIO’s connected to each timer ?
The timer has its input set to
TIMER_CCMR_CCS_INPUT_TI1
But I don’t see how the input GPIO pin is routed to this ??
Edit.
I now see
#define RX1 PA0 //t2c1
Just to answer my own question…
As far as I can see from
http://www.st.com/content/ccc/resource/ … 161566.pdf
These are the timers which have an ETR pin
Timer1 PA12 or PE7 (remap)
Timer2 PA0 or PA15 (remap)
Timer3 PD2
Timer4 PE0
But for the Blue Pill and Maple mini etc (F103Cx) only Timer2 can be used if you are using the bootloader, because PA12 (Timer1) is one of the USB signal s.
The remapping of the ETR seems to be controlled by the register AFIO_MAPR (see section 9.4.2 of the reference manual, on page 183)
This has remapping for Timers 1, 2,3 and 4
Bit 12 TIM4_REMAP: TIM4 remapping
This bit is set and cleared by software. It controls the mapping of TIM4 channels 1 to 4 onto
the GPIO ports.
0: No remap (TIM4_CH1/PB6, TIM4_CH2/PB7, TIM4_CH3/PB8, TIM4_CH4/PB9)
1: Full remap (TIM4_CH1/PD12, TIM4_CH2/PD13, TIM4_CH3/PD14, TIM4_CH4/PD15)
Note: TIM4_ETR on PE0 is not re-mapped.
Bits 11:10 TIM3_REMAP[1:0]: TIM3 remapping
These bits are set and cleared by software. They control the mapping of TIM3 channels 1 to
4 on the GPIO ports.
00: No remap (CH1/PA6, CH2/PA7, CH3/PB0, CH4/PB1)
01: Not used
10: Partial remap (CH1/PB4, CH2/PB5, CH3/PB0, CH4/PB1)
11: Full remap (CH1/PC6, CH2/PC7, CH3/PC8, CH4/PC9)
Note: TIM3_ETR on PE0 is not re-mapped.
Bits 9:8 TIM2_REMAP[1:0]: TIM2 remapping
These bits are set and cleared by software. They control the mapping of TIM2 channels 1 to
4 and external trigger (ETR) on the GPIO ports.
00: No remap (CH1/ETR/PA0, CH2/PA1, CH3/PA2, CH4/PA3)
01: Partial remap (CH1/ETR/PA15, CH2/PB3, CH3/PA2, CH4/PA3)
10: Partial remap (CH1/ETR/PA0, CH2/PA1, CH3/PB10, CH4/PB11)
11: Full remap (CH1/ETR/PA15, CH2/PB3, CH3/PB10, CH4/PB11)
Bits 7:6 TIM1_REMAP[1:0]: TIM1 remapping
These bits are set and cleared by software. They control the mapping of TIM1 channels 1 to
4, 1N to 3N, external trigger (ETR) and Break input (BKIN) on the GPIO ports.
00: No remap (ETR/PA12, CH1/PA8, CH2/PA9, CH3/PA10, CH4/PA11, BKIN/PB12,
CH1N/PB13, CH2N/PB14, CH3N/PB15)
01: Partial remap (ETR/PA12, CH1/PA8, CH2/PA9, CH3/PA10, CH4/PA11, BKIN/PA6,
CH1N/PA7, CH2N/PB0, CH3N/PB1)
10: not used
11: Full remap (ETR/PE7, CH1/PE9, CH2/PE11, CH3/PE13, CH4/PE14, BKIN/PE15,
CH1N/PE8, CH2N/PE10, CH3N/PE12)
The remaping can already be handled by
afio_remap( AFIO_REMAP_TIM2_PARTIAL_1); //etc
I think this may need to be handled by a new gpio_xxxx function, as would reading the value from the timer
static inline uint16 timer_get_compare(timer_dev *dev, uint8 channel) {
__io uint32 *ccr = &(dev->regs).gen->CCR1 + (channel - 1);
return *ccr;
}
I’ve added this functionality to the Core
But. I still need to decide how to add input polarity selection
I’ve included an example in the Sensors section, as thats where the Hardware Timer Encoder example is
Grab the latest version of the repo and you can try the example
(Now on to what I really wanted which is the “One Pulse” feature ![]()
/* Example of the Timer Input Capture mode
*
* Thanks to @cesco on the stm32duino.com
*
* This example uses Timer2. Other timers also support this mode, bnt on the F103C
* only Timer 2 has usable external trigger pins. PA0 and PA15
* Because the only other usable pin on the F103C is one of the USB pins.
*
* Upload to your favourite F103Cx
* Open the Serial plotter
* Put your finger on either PA0 or PA15 (depenidng whether you USE the pin remapping)
*
* Observe your local mains frequency in terms of pulse width time.
*
*/
/* Timer2 external trigger can be ether PA0 or PA15.
To use use afio_remap(FIO_REMAP_TIM2_PARTIAL_1) or afio_remap(AFIO_REMAP_TIM2_FULL)
Note. using afio_remap also changes the pins used by Timer2 CH1,CH2,CH3 amd CH4
* See page 184 of the STM32F10x Refernce manual (PDF downloadable from ST.com
*/
//#define USE_REMAP_PIN
#ifdef USE_REMAP_PIN
const int timer2ExternalTriggerPin = PA15;
#else
const int timer2ExternalTriggerPin = PA0;
#endif
volatile boolean hasCapture;
volatile uint16_t captureValue;
volatile uint16_t lastCaptureValue=0;
volatile uint16_t captureDifference=0;
void captureCallback(void) {
hasCapture=true;
captureValue = Timer2.getCompare(1);
captureDifference = captureValue-lastCaptureValue;
lastCaptureValue=captureValue;
}
void setup() {
pinMode(timer2ExternalTriggerPin,INPUT);
#ifdef USE_REMAP_PIN
afio_remap(AFIO_REMAP_TIM2_PARTIAL_1);
#endif
hasCapture = false;
Timer2.setPrescaleFactor(72); // prescaler to count 72,000,000 / 72 counts per second. i.e count microseconds
Timer2.setMode(TIMER_CH1, TIMER_INPUT_CAPTURE);
Timer2.attachCompare1Interrupt(captureCallback);
}
void loop() {
// Printing inside the callback is not a good idea.
// Hence the capture callback sets a flag , which is detected here
if (hasCapture)
{
Serial.println(captureDifference);
hasCapture=false;
}
}
Anyway, reading capture values within ISR is not the best method to get precise values.
I just used the code provided by @Cesco
But I now see that it has problems
I think the config is wrong.
The counter seems to free run and the ISR gets called when the input pin changes.
Looking at the original code (from Leaflabs) they thought that there would be a pinMode() for INPUT_CAPTURE, but as you have pointed out on GitHub this only allows very limited functionality
So perhaps this is not the correct way to add this functionality, and instead perhaps the Hardware Timer class should have additional functions
e.g.
TimerX.setInputCaptureMode(args …..)
and also
TimerX.setOnePulseMode(args….)
This would then allow better configuration.
This is from @konczakp, not @cesco.
Anyway, you are right, it misses to set the timer in slave mode reset mode (SMS = b100) and to select the right trigger, so that the CCR1 register value is steady after the input signal trigger occurred.
See RM0008 rev.16, p. 383-384, chapter 15.3.6 PWM input mode:
For example, you can measure the period (in TIMx_CCR1 register) and the duty cycle (in TIMx_CCR2 register) of the PWM applied on TI1 using the following procedure (depending on CK_INT frequency and prescaler value):
• Select the active input for TIMx_CCR1: write the CC1S bits to 01 in the TIMx_CCMR1 register (TI1 selected).
• Select the active polarity for TI1FP1 (used both for capture in TIMx_CCR1 and counter clear): write the CC1P to ‘0’ (active on rising edge).
• Select the active input for TIMx_CCR2: write the CC2S bits to 10 in the TIMx_CCMR1 register (TI1 selected).
• Select the active polarity for TI1FP2 (used for capture in TIMx_CCR2): write the CC2P bit to ‘1’ (active on falling edge).
• Select the valid trigger input: write the TS bits to 101 in the TIMx_SMCR register (TI1FP1 selected).
• Configure the slave mode controller in reset mode: write the SMS bits to 100 in the TIMx_SMCR register.
• Enable the captures: write the CC1E and CC2E bits to ‘1 in the TIMx_CCER register.
Me, I am using duty cycle measuring in one of my projects according to this description.
The reference manual has too many examples (with many different options) to be all supported / implemented in the core.
I really cannot see how the HardwareTimer class should be changed to support them.
It would be nice if we would have one example sketch for all of them, even with direct register settings. But I think no one has the necessary time.
I’ll look at that other code
– input capture + one pulse mode
– PWM mode (using input capture) to measure PWM pulse and period width.
I will try your code later today.
I’ve manually copied your updated files and your HardwareTimerPWMInput demo worked fine.
I’ve not tested this copy of your One Pulse demo, but I presume its the same as the one you posted to the forum, so I will test later to confirm its OK
Your files also included my old demo using @konzpcap’s code, but I think I will delete it, as its the wrong way to do input capture
I may also modify your One Pulse demo to show you to use an external trigger for to the timer
Then I’ll commit the changes back to my repo.
BTW. I presume that you can’t do a PR because your repo is too different from mine.
Thanks
Roger
[RogerClark – Mon Feb 12, 2018 7:17 am] –
BTW. I presume that you can’t do a PR because your repo is too different from mine.
Indeed, and because Github is still a mystery for me, I didn’t even offer to do a PR. I can do it for individual files on web github, but not for more than one. Sorry.
It wasn’t too many files to copy manually ![]()
I’ve pushed the updated files from @stevestrong, which work correctly for Input Capture and OnePulse

