Skip to content
This repository was archived by the owner on Jan 29, 2023. It is now read-only.

Commit d1f7bd5

Browse files
authored
v1.8.0 to fix multiple-definitions linker error
### Releases v1.8.0 1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories 2. Add example `Argument_Complex_Multi` to demonstrate how to avoid `multiple-definitions` linker error in multiple-file projects
1 parent 51a5590 commit d1f7bd5

29 files changed

+1247
-881
lines changed

CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ If you don't find anything, please [open a new issue](https://github.com/khoih-p
1414

1515
Please ensure to specify the following:
1616

17-
* Arduino IDE version (e.g. 1.8.16) or Platform.io version
17+
* Arduino IDE version (e.g. 1.8.19) or Platform.io version
1818
* `Arduino AVR` or `Adafruit AVR` Core Version (e.g. Arduino AVR core v1.8.3 or Adafruit AVR Core v1.4.14)
1919
* Contextual information (e.g. what you were trying to achieve)
2020
* Simplest possible steps to reproduce
@@ -26,10 +26,10 @@ Please ensure to specify the following:
2626
### Example
2727

2828
```
29-
Arduino IDE version: 1.8.16
29+
Arduino IDE version: 1.8.19
3030
Arduino AVR Core Version 1.8.3
3131
OS: Ubuntu 20.04 LTS
32-
Linux xy-Inspiron-3593 5.4.0-90-generic #101-Ubuntu SMP Fri Oct 15 20:00:55 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
32+
Linux xy-Inspiron-3593 5.4.0-96-generic #109-Ubuntu SMP Wed Jan 12 16:49:16 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
3333
3434
Context:
3535
I encountered a crash while trying to use the Timer Interrupt.

README.md

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
* [ 10. SwitchDebounce](examples/SwitchDebounce)
5151
* [ 11. TimerDuration](examples/TimerDuration)
5252
* [ 12. TimerInterruptTest](examples/TimerInterruptTest)
53-
* [ 13. Change_Interval_HF](examples/Change_Interval_HF). **New**
53+
* [ 13. Change_Interval_HF](examples/Change_Interval_HF).
54+
* [ 14. Argument_Complex_Multi](examples/Argument_Complex_Multi). **New**
5455
* [Example ISR_16_Timers_Array_Complex](#example-isr_16_timers_array_complex)
5556
* [Debug Terminal Output Samples](#debug-terminal-output-samples)
5657
* [1. ISR_16_Timers_Array_Complex on Arduino AVR Nano-V3 board](#1-isr_16_timers_array_complex-on-arduino-avr-nano-v3-board)
@@ -167,24 +168,26 @@ Another way to install is to:
167168

168169
### HOWTO Fix `Multiple Definitions` Linker Error
169170

170-
The current library implementation, using **xyz-Impl.h instead of standard xyz.cpp**, possibly creates certain `Multiple Definitions` Linker error in certain use cases. Although it's simple to just modify several lines of code, either in the library or in the application, the library is adding 2 more source directories
171+
The current library implementation, using `xyz-Impl.h` instead of standard `xyz.cpp`, possibly creates certain `Multiple Definitions` Linker error in certain use cases.
171172

172-
1. **scr_h** for new h-only files
173-
2. **src_cpp** for standard h/cpp files
173+
You can use
174174

175-
besides the standard **src** directory.
175+
```
176+
#include <TimerInterrupt.hpp> //https://github.com/khoih-prog/TimerInterrupt
177+
#include <ISR_Timer.hpp> //https://github.com/khoih-prog/TimerInterrupt
178+
```
176179

177-
To use the **old standard cpp** way, locate this library' directory, then just
180+
in many files. But be sure to use the following `#include <TimerInterrupt.h>` or `#include <ISR_Timer.h>` **in just 1 `.h`, `.cpp` or `.ino` file**, which must **not be included in any other file**, to avoid `Multiple Definitions` Linker Error
178181

179-
1. **Delete the all the files in src directory.**
180-
2. **Copy all the files in src_cpp directory into src.**
181-
3. Close then reopen the application code in Arduino IDE, etc. to recompile from scratch.
182+
```
183+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
184+
#include "TimerInterrupt.h" //https://github.com/khoih-prog/TimerInterrupt
182185
183-
To re-use the **new h-only** way, just
186+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
187+
#include "ISR_Timer.h" //https://github.com/khoih-prog/TimerInterrupt
188+
```
184189

185-
1. **Delete the all the files in src directory.**
186-
2. **Copy the files in src_h directory into src.**
187-
3. Close then reopen the application code in Arduino IDE, etc. to recompile from scratch.
190+
Check new [**Argument_Complex_Multi** example](examples/Argument_Complex_Multi) for the demo how to avoid `Multiple Definitions` Linker Error.
188191

189192
---
190193
---
@@ -452,7 +455,8 @@ void setup()
452455
10. [SwitchDebounce](examples/SwitchDebounce)
453456
11. [TimerDuration](examples/TimerDuration)
454457
12. [TimerInterruptTest](examples/TimerInterruptTest)
455-
13. [**Change_Interval_HF**](examples/Change_Interval_HF). New.
458+
13. [**Change_Interval_HF**](examples/Change_Interval_HF).
459+
14. [**Argument_Complex_Multi**](examples/Argument_Complex_Multi). **New**
456460

457461
---
458462

@@ -478,7 +482,10 @@ void setup()
478482
#warning Using Timer3
479483
#endif
480484

485+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
481486
#include "TimerInterrupt.h"
487+
488+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
482489
#include "ISR_Timer.h"
483490

484491
#include <SimpleTimer.h> // https://github.com/schinken/SimpleTimer
@@ -821,7 +828,7 @@ While software timer, **programmed for 2s, is activated after more than 10.000s
821828
```
822829

823830
Starting ISR_16_Timers_Array_Complex on Arduino AVR UNO, Nano, etc.
824-
TimerInterrupt v1.7.0
831+
TimerInterrupt v1.8.0
825832
CPU Frequency = 16 MHz
826833
Starting ITimer1 OK, millis() = 7
827834
SimpleTimer : 2, ms : 10007, Dms : 10007
@@ -971,7 +978,7 @@ The following is the sample terminal output when running example [Change_Interva
971978
972979
```
973980
Starting Change_Interval on Arduino AVR Mega2560/ADK
974-
TimerInterrupt v1.7.0
981+
TimerInterrupt v1.8.0
975982
CPU Frequency = 16 MHz
976983
Starting ITimer1 OK, millis() = 5
977984
Starting ITimer3 OK, millis() = 8
@@ -999,7 +1006,7 @@ The following is the sample terminal output when running example [Change_Interva
9991006
10001007
```
10011008
Starting Change_Interval_HF on Arduino AVR UNO, Nano, etc.
1002-
TimerInterrupt v1.7.0
1009+
TimerInterrupt v1.8.0
10031010
CPU Frequency = 16 MHz
10041011
[TISR] T1
10051012
[TISR] Freq * 1000 = 5000000.00
@@ -1042,7 +1049,7 @@ The following is the sample terminal output when running example [Change_Interva
10421049
```
10431050

10441051
Starting Change_Interval_HF on Arduino AVR Mega2560/ADK
1045-
TimerInterrupt v1.7.0
1052+
TimerInterrupt v1.8.0
10461053
CPU Frequency = 16 MHz
10471054
[TISR] T1
10481055
[TISR] Freq * 1000 = 5000000.00
@@ -1138,6 +1145,8 @@ Submit issues to: [TimerInterrupt issues](https://github.com/khoih-prog/TimerInt
11381145
13. Add Timer3 and Timer4 support to **ATmega32U4 and ATmega16U4**.
11391146
14. Fix bug resulting half frequency when using high frequencies.
11401147
15. Fix bug resulting wrong frequency for some low frequencies.
1148+
16. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories
1149+
11411150

11421151
---
11431152
---

changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
## Table of Contents
1212

1313
* [Changelog](#changelog)
14+
* [Releases v1.8.0](#releases-v180)
1415
* [Releases v1.7.0](#releases-v170)
1516
* [Releases v1.6.0](#releases-v160)
1617
* [Releases v1.5.0](#releases-v150)
@@ -28,6 +29,11 @@
2829

2930
## Changelog
3031

32+
### Releases v1.8.0
33+
34+
1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories
35+
2. Add example `Argument_Complex_Multi` to demonstrate how to avoid `multiple-definitions` linker error in multiple-file projects
36+
3137
### Releases v1.7.0
3238

3339
1. Fix bug resulting wrong frequency for some low frequencies.

examples/Argument_Complex/Argument_Complex.ino

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#warning Using Timer3
4343
#endif
4444

45+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
4546
#include "TimerInterrupt.h"
4647

4748
#if !defined(LED_BUILTIN)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/****************************************************************************************************************************
2+
Argument_Complex_Multi.cpp
3+
For Arduino and Adadruit AVR 328(P) and 32u4 boards
4+
Written by Khoi Hoang
5+
6+
Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt
7+
Licensed under MIT license
8+
9+
Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer.
10+
Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds)
11+
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
12+
Therefore, their executions are not blocked by bad-behaving functions / tasks.
13+
This important feature is absolutely necessary for mission-critical tasks.
14+
15+
Notes:
16+
Special design is necessary to share data between interrupt code and the rest of your program.
17+
Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
18+
variable can not spontaneously change. Because your function may change variables while your program is using them,
19+
the compiler needs this hint. But volatile alone is often not enough.
20+
When accessing shared variables, usually interrupts must be disabled. Even with volatile,
21+
if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
22+
If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
23+
or the entire sequence of your code which accesses the data.
24+
*****************************************************************************************************************************/
25+
26+
// To demonstrate the usage of complex multiple files to avoid `multi definition linker` error
27+
// by using TimerInterrupt.hpp in multiple files but TimerInterrupt.h in only main file
28+
29+
#include "Argument_Complex_Multi.h"
30+
31+
void TimerHandler(unsigned int outputPinsAddress)
32+
{
33+
static bool toggle = false;
34+
35+
//timer interrupt toggles pins
36+
#if (TIMER_INTERRUPT_DEBUG > 1)
37+
Serial.print("Toggle pin1 = "); Serial.println( ((pinStruct *) outputPinsAddress)->Pin1 );
38+
#endif
39+
40+
digitalWrite(((pinStruct *) outputPinsAddress)->Pin1, toggle);
41+
42+
#if (TIMER_INTERRUPT_DEBUG > 1)
43+
Serial.print("Read pin2 A0 ("); Serial.print(((pinStruct *) outputPinsAddress)->Pin2 );
44+
Serial.print(") = ");
45+
Serial.println(digitalRead(((pinStruct *) outputPinsAddress)->Pin2) ? "HIGH" : "LOW" );
46+
47+
Serial.print("Read pin3 A1 ("); Serial.print(((pinStruct *) outputPinsAddress)->Pin3 );
48+
Serial.print(") = ");
49+
Serial.println(digitalRead(((pinStruct *) outputPinsAddress)->Pin3) ? "HIGH" : "LOW" );
50+
#endif
51+
52+
toggle = !toggle;
53+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/****************************************************************************************************************************
2+
Argument_Complex_Multi.h
3+
For Arduino and Adadruit AVR 328(P) and 32u4 boards
4+
Written by Khoi Hoang
5+
6+
Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt
7+
Licensed under MIT license
8+
9+
Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer.
10+
Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds)
11+
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
12+
Therefore, their executions are not blocked by bad-behaving functions / tasks.
13+
This important feature is absolutely necessary for mission-critical tasks.
14+
15+
Notes:
16+
Special design is necessary to share data between interrupt code and the rest of your program.
17+
Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
18+
variable can not spontaneously change. Because your function may change variables while your program is using them,
19+
the compiler needs this hint. But volatile alone is often not enough.
20+
When accessing shared variables, usually interrupts must be disabled. Even with volatile,
21+
if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
22+
If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
23+
or the entire sequence of your code which accesses the data.
24+
*****************************************************************************************************************************/
25+
26+
// To demonstrate the usage of complex multiple files to avoid `multi definition linker` error
27+
// by using TimerInterrupt.hpp in multiple files but TimerInterrupt.h in only main file
28+
29+
#ifndef Argument_Complex_Multi_h
30+
#define Argument_Complex_Multi_h
31+
32+
// These define's must be placed at the beginning before #include "TimerInterrupt.h"
33+
// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
34+
// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
35+
#define TIMER_INTERRUPT_DEBUG 0
36+
#define _TIMERINTERRUPT_LOGLEVEL_ 3
37+
38+
#if ( defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || \
39+
defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_MINI) || defined(ARDUINO_AVR_ETHERNET) || \
40+
defined(ARDUINO_AVR_FIO) || defined(ARDUINO_AVR_BT) || defined(ARDUINO_AVR_LILYPAD) || defined(ARDUINO_AVR_PRO) || \
41+
defined(ARDUINO_AVR_NG) || defined(ARDUINO_AVR_UNO_WIFI_DEV_ED) || defined(ARDUINO_AVR_DUEMILANOVE) || defined(ARDUINO_AVR_FEATHER328P) || \
42+
defined(ARDUINO_AVR_METRO) || defined(ARDUINO_AVR_PROTRINKET5) || defined(ARDUINO_AVR_PROTRINKET3) || defined(ARDUINO_AVR_PROTRINKET5FTDI) || \
43+
defined(ARDUINO_AVR_PROTRINKET3FTDI) )
44+
#define USE_TIMER_1 true
45+
#warning Using Timer1
46+
#else
47+
#define USE_TIMER_3 true
48+
#warning Using Timer3
49+
#endif
50+
51+
// Can be included in many files without `Multiple Definitions` Linker Error
52+
#include "TimerInterrupt.hpp"
53+
54+
#if !defined(LED_BUILTIN)
55+
#define LED_BUILTIN 13
56+
#endif
57+
58+
struct pinStruct
59+
{
60+
unsigned int Pin1;
61+
unsigned int Pin2;
62+
unsigned int Pin3;
63+
};
64+
65+
#define TIMER_INTERVAL_MS 1000
66+
67+
void TimerHandler(unsigned int outputPinsAddress);
68+
69+
#endif // Argument_Complex_Multi_h
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/****************************************************************************************************************************
2+
Argument_Complex_Multi.ino
3+
For Arduino and Adadruit AVR 328(P) and 32u4 boards
4+
Written by Khoi Hoang
5+
6+
Built by Khoi Hoang https://github.com/khoih-prog/TimerInterrupt
7+
Licensed under MIT license
8+
9+
Now we can use these new 16 ISR-based timers, while consuming only 1 hardware Timer.
10+
Their independently-selected, maximum interval is practically unlimited (limited only by unsigned long miliseconds)
11+
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
12+
Therefore, their executions are not blocked by bad-behaving functions / tasks.
13+
This important feature is absolutely necessary for mission-critical tasks.
14+
15+
Notes:
16+
Special design is necessary to share data between interrupt code and the rest of your program.
17+
Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
18+
variable can not spontaneously change. Because your function may change variables while your program is using them,
19+
the compiler needs this hint. But volatile alone is often not enough.
20+
When accessing shared variables, usually interrupts must be disabled. Even with volatile,
21+
if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
22+
If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
23+
or the entire sequence of your code which accesses the data.
24+
*****************************************************************************************************************************/
25+
26+
// To demonstrate the usage of complex multiple files to avoid `multi definition linker` error
27+
// by using TimerInterrupt.hpp in multiple files but TimerInterrupt.h in only main file
28+
29+
// These definitions must be placed before #include <TimerInterrupt.h>
30+
#include "Argument_Complex_Multi.h"
31+
32+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
33+
#include "TimerInterrupt.h"
34+
35+
volatile pinStruct myOutputPins = { LED_BUILTIN, A0, A1 };
36+
37+
#define TIMER_INTERVAL_MS 1000
38+
39+
void setup()
40+
{
41+
pinMode(myOutputPins.Pin1, OUTPUT);
42+
pinMode(myOutputPins.Pin2, OUTPUT);
43+
pinMode(myOutputPins.Pin3, OUTPUT);
44+
45+
Serial.begin(115200);
46+
while (!Serial);
47+
48+
Serial.print(F("\nStarting Argument_Complex on "));
49+
Serial.println(BOARD_TYPE);
50+
Serial.println(TIMER_INTERRUPT_VERSION);
51+
Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz"));
52+
53+
// Timer0 is used for micros(), millis(), delay(), etc and can't be used
54+
// Select Timer 1-2 for UNO, 1-5 for MEGA, 1,3,4 for 16u4/32u4
55+
// Timer 2 is 8-bit timer, only for higher frequency
56+
// Timer 4 of 16u4 and 32u4 is 8/10-bit timer, only for higher frequency
57+
58+
// Using ATmega328 used in UNO => 16MHz CPU clock ,
59+
// For 16-bit timer 1, 3, 4 and 5, set frequency from 0.2385 to some KHz
60+
// For 8-bit timer 2 (prescaler up to 1024, set frequency from 61.5Hz to some KHz
61+
62+
#if USE_TIMER_1
63+
64+
ITimer1.init();
65+
66+
// Using ATmega328 used in UNO => 16MHz CPU clock ,
67+
68+
if (ITimer1.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, (unsigned int) &myOutputPins))
69+
{
70+
Serial.print(F("Starting ITimer1 OK, millis() = ")); Serial.println(millis());
71+
}
72+
else
73+
Serial.println(F("Can't set ITimer1. Select another freq. or timer"));
74+
75+
#elif USE_TIMER_3
76+
77+
ITimer3.init();
78+
79+
if (ITimer3.attachInterruptInterval(TIMER_INTERVAL_MS, TimerHandler, (unsigned int) &myOutputPins))
80+
{
81+
Serial.print(F("Starting ITimer3 OK, millis() = ")); Serial.println(millis());
82+
}
83+
else
84+
Serial.println(F("Can't set ITimer3. Select another freq. or timer"));
85+
86+
#endif
87+
}
88+
89+
void loop()
90+
{
91+
}

examples/Argument_None/Argument_None.ino

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#warning Using Timer1, Timer3
4545
#endif
4646

47+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
4748
#include "TimerInterrupt.h"
4849

4950
#define TIMER1_INTERVAL_MS 1000

examples/Argument_Simple/Argument_Simple.ino

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#warning Using Timer3
4545
#endif
4646

47+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
4748
#include "TimerInterrupt.h"
4849

4950
#if !defined(LED_BUILTIN)

0 commit comments

Comments
 (0)