2

I'm currently working on a PWM modulator to "simulate" a car engine ignition commutation. Then, I will use it to drive another microcontroller which handles the conversion from a raw signal (engine's commutator) to a clean output voltage, going through the RPM-counter's galvanometer.

This project is also a pretext for me to learn how to have a better control upon my microcontroller.

Well, I wrote a small program, using timer0 (8 bits), and I need to trigger two interrupt service routines (ISRs):

  • TIMER0_OVF_vect: overflow interruption
  • TIMER0_COMPA_vect: fires on compare

I have the following functions:

void configureTimer0(parameters)
{
    cli();

    // Some maths
    TCCR0A = (1<<WGM01) | (1<<WGM00); // I tried to use the "Fast PWM" waveform generation mode
    TCCR0B &= 0b00110000; // 5th and 4th bits are reserved. Every other bits is set to 0.
    TCNT0 = 0; // Initialize counter value to 0

    TCCR0B |= select_prescaler(prescaler); // Custom function to determine the right prescaler
    OCR0A = ext_OnTicks; // Output compare register is set to a predetermined numbers of ticks

    // Enabling overflows interrupt:
    TIMSK0 |= (1 << TOIE0);
    sei(); // Enable interrupts
}

Then, under certain conditions, I switch the OCIE0 bit in TIMSK0 on and off depending on the situation. However, the overflow interruption is always enabled.

When I try to compile, I end up with these errors:

"C:\Users\UTILIS~1\AppData\Local\Temp\arduino_build_8383\sketch\PWM_modulator.ino.cpp.o" "C:\Users\UTILIS~1\AppData\Local\Temp\arduino_build_8383/..\arduino_cache_395570\core\core_arduino_avr_uno_a94ab6aaf61dfb93b4a8079c694a14c2.a" "-LC:\Users\UTILIS~1\AppData\Local\Temp\arduino_build_8383" -lm
wiring.c.o (symbol from plugin): In function `__vector_16':

(.text+0x0): multiple definition of `__vector_16'

C:\Users\UTILIS~1\AppData\Local\Temp\arduino_build_8383\sketch\PWM_modulator.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

collect2.exe: error: ld returned 1 exit status

exit status 1
Erreur de compilation pour la carte Arduino/Genuino Uno // Compilation error for Uno board (French)

I tried to compile with ISR(TIMER0_COMPB_vect) instead of ISR(TIMER0_OVF_vect), and it works, but indeed it's not the point of my program.

I also tried ISR(TIMER2_OVF_vect) which is an 8-bit timer as well. I could synchronize Timer0 and Timer2 to achieve the desired effect, however I don't think it is quite clean to do so (and it prevents me to use the Timer2 capabilities).

There is something wrong in my code, but cannot understand where the mistake is hiding. There is definitely something related to the wiring.c.o file.

PS: I've looked for a solution quite a long time since the problem appears, but I could not find any way to ask Google correctly neither the Stack Overflow site. Apologies if I missed the right search string!

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131

3 Answers3

4

It is possible to provide your own handler for Timer0 and still use the Arduino environment.

This is done by (optionally) disabling the existing handler for Timer0 in file wiring.c (inside the Arduino installation). Put in conditional compilation, #ifndef / #endif, around the existing handler:

#ifndef _DISABLE_ARDUINO_TIMER0_INTERRUPT_HANDLER_

    #if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ISR(TIM0_OVF_vect)
    #else
    ISR(TIMER0_OVF_vect)
    #endif
    {
        // Copy these to local variables so they can be stored in registers
        // (volatile variables must be read from memory on every access)
        unsigned long m = timer0_millis;
        unsigned char f = timer0_fract;

        m += MILLIS_INC;
        f += FRACT_INC;
        if (f >= FRACT_MAX) {
            f -= FRACT_MAX;
            m += 1;
        }

        timer0_fract = f;
        timer0_millis = m;
        timer0_overflow_count++;
    }

#endif

For example, on Windows, file wiring.c may be located in folder C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino.

Note that it may be necessary to have administrative privileges to make changes to wiring.c. On Windows this is done by right clicking on the text editor (a text editor, like Notepad++ or UltraEdit[1], that can handle the Unix end-of-line characters - Notepad can not really handle this) and selecting "Run as Adminstrator".

Then, in your source code put these two lines at the top of the file:

#define _DISABLE_ARDUINO_TIMER0_INTERRUPT_HANDLER_

#include <wiring.c>

Note that for other Arduino projects it works exactly as before. The interrupt handler is only disabled if preprocessor symbol _DISABLE_ARDUINO_TIMER0_INTERRUPT_HANDLER_ is defined.

The same procedure may have to be applied to TIMER0_COMPA_vect as well (file Tone.cpp in the same folder).

This was tested with an Arduino Uno equivalent, Arduino IDE 1.8.5, text editor UltraEdit, and Windows 10.

1. I would also expect Visual Studio Code to work, but I haven't tested it.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • Thanks a lot for the reply. Since the days I asked this, I dove into AtmelStudio and the magic of optimizations and simulations. Now it's all clearer to me. I will certainly use your trick for arduino-only programs, that could help alot and save a bunch of time / effort (redesigning a program to use Timer1 instead of Timer0 might be a pain). – bebenlebricolo Feb 15 '18 at 18:02
  • @bebenlebricolo: I found an unintrusive way to do this (so other Arduino projects are not affected by the change in file `wiring.c`). I have updated my answer. (I ran into the same problem as you when writing software for an accurate 8 MHz frequency counter (no external components required). I had to use Timer0 and Timer1, taking advantage to those two hardware counters, using Timer0 for the input signal. Timer2 was used for a test signal of 1.14285714286 MHz.) – Peter Mortensen Feb 15 '18 at 20:12
  • @bebenlebricolo: Whoa! I just discovered that [the Arduino Uno uses a ceramic resonator](https://learn.adafruit.com/arduino-tips-tricks-and-techniques/arduino-uno-faq) (+/-0.5%), not a crystal oscillator (there is a crystal oscillator on the PCB, but that is for the USB chip). I have had bad experiences with ceramic resonators at work (used by a third-party supplier). The final version of the frequency counter will be for a bare ATmega328 chip, with a crystal oscillator (and maybe later a DCF77 based frequency reference). – Peter Mortensen Feb 15 '18 at 21:20
1

You're using the Arduino library, which already defines a handler for the TIMER0_OVF interrupt.

If you want to define your own handler for this interrupt, you will need to build your project standalone, without using the Arduino library or tools.

  • This is true... So this is mainly due to the Arduino library? I didn't think i had to jump to avr-gcc and avr toolchain so early, but anyway it HAVE to try it! Thank you for your reply! – bebenlebricolo Oct 04 '17 at 21:09
0

I simply used Timer2 instead of Timer0. Using Timer2, I can still set an overflow AND an output compare interruptions on the same timer, as expected.

This is a rather simple way to workaround this problem, however it might not suit all cases. I may need to rework my code and adapt it to a direct avr-gcc / Atmel Studio compilation process.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131