-
Notifications
You must be signed in to change notification settings - Fork 12
/
EventTimer_AtMega.h
175 lines (152 loc) · 6.07 KB
/
EventTimer_AtMega.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/* Copyright (c) 2021 Neil McKechnie
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software. If not, see
* <http://www.gnu.org/licenses/>.
*/
/*
* EventTimer_AtMega.h
*
* Timer functions for measuring elapsed time between events. On the
* AtMega328 and AtMega2560 controllers, the functions use the Input Capture
* capability of the microcontroller to capture the timer value very precisely
* using hardware.
*
* Since the Timer counter is limited to 16 bits, it will overflow if the interval exceeds
* about 32milliseconds. To avoid this, we check micros() to determine if pulse
* length is too long and, in that case, calculate the ticks from micros().
*
*/
#ifndef eventtimer_default_h
#define eventtimer_default_h
#include <Arduino.h>
#include "Config.h"
#define GPIO_PREFER_SPEED
#include <DIO2.h>
#define TICKSPERMICROSEC 2 // 2 (0.5us) or 16 (62.5ns)
#if defined(ARDUINO_UNO_NANO)
#define ICP_INPUTPIN 8
#define TCNTn TCNT1 // TimerN Counter Register
#define ICRn ICR1 // TimerN Input Change Register
#define TCCRnA TCCR1A // TimerN configuration register
#define TCCRnB TCCR1B // TimerN configuration register
#define TIMSKn TIMSK1 // TimerN interrupt register
#define ICIEn ICIE1 // Interrupt mask
#define ICESn ICES1 // Mask
#define CAPTURE_INTERRUPT TIMER1_CAPT_vect // ISR vector
#elif defined (ARDUINO_MEGA)
#define ICP_INPUTPIN 49
#define TCNTn TCNT4 // TimerN Counter Register
#define ICRn ICR4 // TimerN Input Change Register
#define TCCRnA TCCR4A // TimerN configuration register
#define TCCRnB TCCR4B // TimerN configuration register
#define TIMSKn TIMSK4 // TimerN interrupt register
#define ICIEn ICIE4 // Interrupt mask
#define ICESn ICES4 // Mask
#define CAPTURE_INTERRUPT TIMER4_CAPT_vect // ISR vector
#else
#error "Architecture not supported by EventTimer library."
#endif
/*
* User event handler is sent the time since the last valid event.
* If the user event handler decides that this event isn't valid
* (e.g. time since last one is too short) then it should return
* false to reject the event. If the event is valid, then it
* should return true to accept it.
*/
typedef bool EventHandler(unsigned long eventInterval);
class EventTimerClass {
public:
// Initialise the object instance, validating that the input pin is
// correct and noting the reference to the user handler for future use.
bool begin(int pin, EventHandler userHandler) {
if (pin != ICP_INPUTPIN) {
Serial.print(F("ERROR: pin="));
Serial.print(pin);
Serial.print(F(", ICP="));
Serial.println(ICP_INPUTPIN);
return false;
}
this->pin = pin;
this->callUserHandler = userHandler;
// Set up input capture.
// Configure Timer to increment TCNTn on a 2MHz clock,
// (every 0.5us), or 16MHz (every 62.5ns), no interrupt.
// Use Input Capture Pin ICP to capture time of input change
// and interrupt the CPU.
TCCRnA = 0;
#if TICKSPERMICROSEC==2
TCCRnB = (1 << CS11); // Prescaler CLK/8
#elif TICKSPERMICROSEC==16
TCCRnB = (1 << CS10); // Prescaler CLK/1
#else
#error "TICKSPERMICROSEC" not 2 or 16.
#endif
TIMSKn = (1 << ICIEn); // Input capture interrupt enable
return true;
};
// Utility function to give number of ticks since the last event. Useful
// for determining how much time has elapsed within the interrupt handler since
// the interrupt was triggered.
inline unsigned long elapsedTicksSinceLastEvent() {
return (unsigned int)(TCNTn - thisEventTicks);
};
// Function called from the interrupt handler to calculate the gap between interrupts,
// and to invoke the user program's handler. The user's handler is passed the
// number of ticks elapsed since the last valid interrupt. It returns true/false to
// indicate if this interrupt is deemed to be 'valid' or not.
void processInterrupt() {
// Time-critical bits.
unsigned long thisEventMicros = micros();
thisEventTicks = ICRn;
byte diginState = digitalRead2(this->pin);
// Set up input capture for next edge.
if (diginState)
TCCRnB &= ~(1 << ICESn); // Capture next falling edge on input
else
TCCRnB |= (1 << ICESn); // Capture next rising edge on input
// Initially estimate time ticks using micros() values.
unsigned long eventSpacing = (thisEventMicros - lastValidEventMicros) * TICKSPERMICROSEC;
if (eventSpacing < 60000L) {
// Estimated time is well within range of timer count, so calculate ticks from ICRn
eventSpacing = thisEventTicks - lastValidEventTicks;
}
// Call user handler.
bool accepted = callUserHandler(eventSpacing);
if (accepted) {
lastValidEventTicks = thisEventTicks;
lastValidEventMicros = thisEventMicros;
}
};
// Utility function to return the number of timer ticks per microsecond.
// On the AtMega, this is 2 or 16, depending on the timer pre-scaler setting.
inline unsigned int ticksPerMicrosec() {
return TICKSPERMICROSEC;
};
// Utility function to inform whether input capture is in use or not. For this
// version, it always returns true.
inline bool inputCaptureMode() { return true; };
private:
EventHandler *callUserHandler = 0;
unsigned int lastValidEventTicks = 0;
unsigned long lastValidEventMicros = 0;
unsigned int thisEventTicks = 0;
int pin = -1;
} /* class EventTimerClass */;
// Declare singleton class instance.
EventTimerClass EventTimer;
// Interrupt handler for input capture event
ISR(CAPTURE_INTERRUPT) {
EventTimer.processInterrupt();
}
#endif