Previous declaration of 'void TIM2_IRQHandler()' with 'C++' linkage / Interrupt ISR Based Timer


I’ve written an app using the local build environment, in which I have set up and used Timer 2 (TIM2) with an attached interrupt. It all compiles and runs fine in the local build environment, but borks in the Web IDE.

The IRQ handler is declared thus …

extern "C" void TIM2_IRQHandler()
  static uint8_t currentDigitNumber = 0;

  if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // clear the interrupt flag

    // do stuff here

The error I get from clicking the Verify button in the Web IDE is …

error: previous declaration of 'void TIM2_IRQHandler()' with 'C++' linkage

If I remove the ‘extern “C”’ part, then the code compiles. But the interrupt routine doesn’t actually run. In fact, nothing in loop() appears to be working, either – even though the core’s main status LED is doing its usual cyan pulsatatificating thing.

It seems to me that IRQ handlers should not use C++ “linkage” (calling conventions.) Perhaps there is a bug in the IDE compiler? Or maybe there’s another #pragma or something I can use to get around this little problem? It sure would be nice to be able to update this app in the field, the Spark IDE way. :smiley:

Let me know if I’m missing something. It has been know to happen, once or twice! :stuck_out_tongue:


Hey @gruvin - replicated this bug on my machine, and it doesn’t seem to be the fault of the Arduino pre-processor, because the issue comes up when it’s added to a library file (which isn’t processed) or when the pre-processor is turned off.

My only thought is that it might have to do with some configuration settings that might differ between the local build and the cloud; I’m discussing with @zachary and @mohit behind the scenes to see what they think

Cool, thanks for all your ongoing efforts. :smiley:

Hey @zach. Is there any news re this interrupt function thing in the Web IDE version? Cheers.

Hey @gruvin - this works now! In order to get this to compile, you need to add it to a library file; it can’t be part of your main .ino file. Then you need to #include "application.h" in that file. If you do that, it should compile as expected!

1 Like

Have you also written some code to initialize the Interrupt? Such as the following for TIM1

From hw_config.c:

        /* Disable TIM1 CC4 Interrupt */
        TIM_ITConfig(TIM1, TIM_IT_CC4, DISABLE);

        /* Enable the TIM1 Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = TIM1_CC_IRQ_PRIORITY;	//OLD: 0x02
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;							//OLD: 0x00
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;


And it’s handler from stm32_it.cpp:

void TIM1_CC_IRQHandler(void)
	if (TIM_GetITStatus(TIM1, TIM_IT_CC4) != RESET)
		TIM_ClearITPendingBit(TIM1, TIM_IT_CC4);

			/* Disable TIM1 CC4 Interrupt */

			/* Enable BUTTON1 Interrupt */
1 Like

Thanks @zach. That’s working.

Since there’s no way to edit application.h in the Web IDE, I had to move all variables and functions relied on by the IRQ handler into the additional library. But that strikes me as a better overall layout anyway – in this case, essentially having a hardware abstraction layer, encapsulated in a library. Yay \o/

@BDub – The application is fully functional using the command line tools. IRQ executes 200x a second. :smile:

Perfect - that makes tons of sense!

@zach … yeah. :smile:

Only, now I’m trying to duplicate the set-up in my command-line tools version and coming unstuck with the makefile.

Far as I can tell from the makefile (in core-firmware/build) it should automagically find all .cpp file in …/src, build and link them. But neither is actually happening. My new ‘library’ is ignored entirely.

My “library” is just an additional .cpp/h file pair, in the same folder/s as application.cpp.

EDIT: AH!! in the src directory. All sorted. Working great. Yay \o/

Sounds good, but I’m not following what you did to enable it. Can you share your complete IRQ setup?

You know guys, it has now become obvious to me that the interval timer pool library I was thinking of is no longer an option! That’s my next project.


Sure. Sorry, I thought you were offering help in the last post, not asking for it. :smiley:

The following sets up the timer itself, to upload/overflow 200x a second …

  // We'll use Timer 2 for LED output digit scanning
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  TIM_TimeBaseInitTypeDef timerInitStructure;
  timerInitStructure.TIM_Prescaler = 45000; // 1,600 counts per second (for finer grain calibration)
  timerInitStructure.TIM_Period = 7; // reload 200 times a second. Measured accuracy well within 5ppm
  timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  timerInitStructure.TIM_RepetitionCounter = 0;
  TIM_TimeBaseInit(TIM2, &timerInitStructure);
  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);  // enable the timer update event, for our interrupt

Note the first line (after the comment.) Only Timer1 gets its clock enabled automatically at power-on-reset. The others need to be explicitly enabled. (Because those timers can have other clock sources, including other timers. Such a cool MCU is this baby!)

And this part, along with TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); above, enables the interrupt …

  // Set up an interrupt handler for the timer 2 reload event, enabled above
  NVIC_InitTypeDef nvicStructure;
  nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
  nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
  nvicStructure.NVIC_IRQChannelSubPriority = 1;
  nvicStructure.NVIC_IRQChannelCmd = ENABLE;

Hope that helps.

gruvin, very cool, I will use this stuff for my timer pool library! :smiley:

@gruvin I was offering help, but I don’t have time to try EVERYTHING myself, so if users like you can post your results… it will help everyone that comes looking for an ISR based TIMER in the future :wink: Thanks!

EDIT: I amended the title to make this easier to find, and moved to FIRMWARE category.