Override existing __irq_tim1_trg_com

Working libraries, libraries being ported and related hardware
Post Reply
tiger762
Posts: 29
Joined: Tue Jan 26, 2016 11:40 pm

Override existing __irq_tim1_trg_com

Post by tiger762 » Mon Dec 18, 2017 5:42 pm

Greetings!

I have a project that measures the PWM output of a MHZ19 CO2 sensor. I need to override the existing IRQ handler for TIM1_TRG_COM. The symbol exists in three places in core.a:

[tiger@den]# nm core.a | grep irq_tim1_trg
00000001 W __irq_tim1_trg_com
U __irq_tim1_trg_com
00000001 T __irq_tim1_trg_com

In isrs.S it is a weak symbol.
In vector_table.S it is Undefined
In timer.c it is in the TEXT section.

void __irq_tim1_trg_com(void) {
dispatch_adv_trg_com(TIMER1);
#if STM32_HAVE_TIMER(11)
dispatch_tim_10_11_13_14(TIMER11);
#endif
}

Not sure what those 'dispatch' functions/macros do. Before tearing into that, I was curious if I could just delete the __irq_tim1_trg_com symbol from core.a?

If I need to I'll recompile timer.c (timer.o is a part of core.a)

Does anyone know of a quick way to accomplish this? Thanks!

tiger762
Posts: 29
Joined: Tue Jan 26, 2016 11:40 pm

Re: Override existing __irq_tim1_trg_com

Post by tiger762 » Mon Dec 18, 2017 7:24 pm

I discovered a workaround. Hopefully this helps someone else in the future.

Suppose you have function() in an archive and want to override that function with one of your own. Rename your version of function() as __wrap_function(). Next, on the link line, add this:

Code: Select all

-Wl,--wrap=function
Your '__wrap_function' will be called, and in it you can call __real_function() if you want to. In my case, I implemented:

Code: Select all

extern "C" void __wrap___irq_tim1_trg_com(void)
and on the link line:

Code: Select all

-Wl,--wrap=__irq_tim1_trg_com
...and it works perfectly :)

Doing an objdump of program.elf, we see in the interrupt vector table:

Code: Select all

08000000 <__stm32_vector_table>:
 8000000:	00 50 00 20 15 05 00 08 25 05 00 08 25 05 00 08     .P. ....%...%...
 8000010:	25 05 00 08 25 05 00 08 25 05 00 08 25 05 00 08     %...%...%...%...
 8000020:	25 05 00 08 25 05 00 08 25 05 00 08 25 05 00 08     %...%...%...%...
 8000030:	25 05 00 08 25 05 00 08 25 05 00 08 b1 07 00 08     %...%...%.......
 8000040:	25 05 00 08 25 05 00 08 25 05 00 08 25 05 00 08     %...%...%...%...
 8000050:	25 05 00 08 25 05 00 08 25 05 00 08 25 05 00 08     %...%...%...%...
 8000060:	25 05 00 08 25 05 00 08 25 05 00 08 25 05 00 08     %...%...%...%...
 8000070:	25 05 00 08 25 05 00 08 25 05 00 08 25 05 00 08     %...%...%...%...
 8000080:	25 05 00 08 25 05 00 08 25 05 00 08 25 05 00 08     %...%...%...%...
 8000090:	25 05 00 08 25 05 00 08 25 05 00 08 25 05 00 08     %...%...%...%...
 80000a0:	29 05 00 08 4d 05 00 08 [b]75 01 00 08[/b] a9 05 00 08     )...M...u.......
 80000b0:	fd 05 00 08 6d 06 00 08 dd 06 00 08 25 05 00 08     ....m.......%...
 80000c0:	25 05 00 08 25 05 00 08 25 05 00 08 25 05 00 08     %...%...%...%...
 80000d0:	25 05 00 08 25 05 00 08 25 05 00 08 25 05 00 08     %...%...%...%...
 80000e0:	25 05 00 08 25 05 00 08 25 05 00 08                 %...%...%...
irq_tim1_trg_com is at offset 0xa8 (above, in bold)

Next, looking in the text section, we see this:

Code: Select all

08000174 <__wrap___irq_tim1_trg_com>:
(truncated for brevity)

This goes to show that the ISR handler now points to the wrapped function that was specified on the link line.

Hope this helps someone else. I spent several hours on this problem ;)

stevestrong
Posts: 2049
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany
Contact:

Re: Override existing __irq_tim1_trg_com

Post by stevestrong » Mon Dec 18, 2017 8:57 pm

Thanks for the hint.

Now I would like to show you how to measure a PWM pulse without overriding system interrupts:

Code: Select all

    // setup timer 2 channels 1 and 2 for servo (TI2).
    // use input capture mode: channel 1 captures on rising edge, channels 2 captures on falling edge
    // the pulse width is the difference between capture 2 and 1.
    pinMode(PA1, INPUT);
    Timer2.pause();
    TIMER2_BASE->CCMR1 = (TIMER_CCMR1_CC1S_INPUT_TI2 | TIMER_CCMR1_CC2S_INPUT_TI2); // no filter, no prescaler
    TIMER2_BASE->CCER  = (TIMER_CCER_CC2P | TIMER_CCER_CC1E | TIMER_CCER_CC2E); // enable channels
    TIMER2_BASE->PSC   = 71; // Timer2.setPrescaleFactor(72); // 1 microsecond resolution
    Timer2.attachInterrupt(TIMER_CH2, Servo_1_pulse_end); // to read out the timer capture value
    Timer2.resume(); // let timer run

#if 0
    // for test: generate PWM signal with Timer 3 channel 1 (PA6)
    pinMode(PA6, PWM);
    Timer3.setPrescaleFactor(22); // 20 millis period (72MHz/65536/50Hz)
    pwmWrite(PA6, 3276); // 1/20th of maximum counts 65536
    // write the negate PWM signal to PC13
    attachInterrupt(PA6, PA6_change_isr, CHANGE);
#endif

tiger762
Posts: 29
Joined: Tue Jan 26, 2016 11:40 pm

Re: Override existing __irq_tim1_trg_com

Post by tiger762 » Mon Dec 18, 2017 10:45 pm

Here's what I'm working with:

Code: Select all

   NVIC_ISER0 |= TIM1_TRG_COM;
   RCC_APB2ENR |= IOPAEN | IOPBEN | IOPCEN | TIM1EN;
   TIM1_CR1   = 0x00;
   TIM1_PSC   = 7199; // Freq1 = 72000000 / (TIM1_PSC+1)
   TIM1_CCMR1 = CC1S_IN_IC1_TI1 | CC2S_IN_IC2_TI1;
   TIM1_CCER  = CC1P_RISING | CC2P_FALLING | CC1E_IN_ENA | CC2E_IN_ENA;
   TIM1_SMCR  = TS_TI1FP1 | SMS_RESET_MODE;
   TIM1_DIER  = TIE;
   TIM1_CR1   |= TIM_ENA;

extern "C" void __wrap___irq_tim1_trg_com (void) {
   if (TIM1_SR & TIF) {
      period = TIM1_CCR1;
      duty = TIM1_CCR2;
      ready = 1;
   }
}
Timer 1 counts at 10KHz. The MHZ-19 CO2 sensor has a period of 1.004 seconds, so the 'period' will always have a count of 10040. The 'duty' will be somewhere between 2000 and 6000. The CO2 PPM is a simple calculation from there. Got it working over the weekend with a 4-digit TM1637 LED display:

https://i.imgur.com/xFAbQj1.jpg?1

The linker trick (above) was just to be able to move the 'bare metal' registers approach to the stm32duino platform :)

Post Reply