forked from leomil72/analogComp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
analogComp.cpp
204 lines (173 loc) · 5.22 KB
/
analogComp.cpp
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/* This file is part of the analogComp library.
Please check the README file and the notes
inside the analogComp.h file
*/
//include required libraries
#include "analogComp.h"
//global variables
typedef void (*userFunc)(void);
volatile static userFunc userFunction;
uint8_t _initialized;
uint8_t _interruptEnabled;
uint8_t oldADCSRA;
//setting and switching on the analog comparator
uint8_t analogComp::setOn(uint8_t tempAIN0, uint8_t tempAIN1) {
if (_initialized) { //already running
return 1;
}
//initialize the analog comparator (AC)
ACSR &= ~(1<<ACIE); //disable interrupts on AC
ACSR &= ~(1<<ACD); //switch on the AC
//choose the input for non-inverting input
if (tempAIN0 == INTERNAL_REFERENCE) {
ACSR |= (1<<ACBG); //set Internal Voltage Reference (1V1)
} else {
ACSR &= ~(1<<ACBG); //set pin AIN0
}
//for Atmega32U4, only ADMUX is allowed as input for AIN-
#ifdef ATMEGAxU
if (tempAIN1 == AIN1) {
tempAIN1 = 0; //choose ADC0
}
#endif
//AtTiny2313/4313 don't have ADC, so inputs are always AIN0 and AIN1
#ifndef ATTINYx313
// allow for channel or pin numbers
#if defined (ATMEGAx0)
if (tempAIN1 >= 54) tempAIN1 -= 54;
#elif defined (ATMEGAxU)
if (tempAIN1 >= 18) tempAIN1 -= 18;
#elif defined (ATMEGAx4)
if (tempAIN1 >= 24) tempAIN1 -= 24;
#elif defined (CORE_ANALOG_FIRST) && (defined (ATTINYx5) || defined (ATTINYx4))
if (tempAIN1 >= CORE_ANALOG_FIRST) {
tempAIN1 -= CORE_ANALOG_FIRST;
}
#else
if (tempAIN1 >= 14) tempAIN1 -= 14;
#endif
//choose the input for inverting input
oldADCSRA = ADCSRA;
if ((tempAIN1 >= 0) && (tempAIN1 < NUM_ANALOG_INPUTS)) { //set the AC Multiplexed Input using an analog input pin
ADCSRA &= ~(1<<ADEN);
ADMUX &= ~31; //reset the first 5 bits
ADMUX |= tempAIN1; //choose the ADC channel (0..NUM_ANALOG_INPUTS-1)
AC_REGISTER |= (1<<ACME);
} else {
AC_REGISTER &= ~(1<<ACME); //set pin AIN1
}
#endif
//disable digital buffer on pins AIN0 && AIN1 to reduce current consumption
#if defined(ATTINYx5)
DIDR0 &= ~((1<<AIN1D) | (1<<AIN0D));
#elif defined(ATTINYx4)
DIDR0 &= ~((1<<ADC2D) | (1<<ADC1D));
#elif defined (ATMEGAx4)
DIDR1 &= ~(1<<AIN0D);
#elif defined (ATTINYx313)
DIDR &= ~((1<<AIN1D) | (1<<AIN0D));
#elif defined (ATMEGAx8) || defined(ATMEGAx4) || defined(ATMEGAx0)
DIDR1 &= ~((1<<AIN1D) | (1<<AIN0D));
#endif
_initialized = 1;
return 0; //OK
}
//enable the interrupt on comparations
void analogComp::enableInterrupt(void (*tempUserFunction)(void), uint8_t tempMode) {
if (_interruptEnabled) { //disable interrupts
SREG &= ~(1<<SREG_I);
ACSR &= ~(1<<ACIE);
}
if (!_initialized) {
setOn(AIN0, AIN1);
}
//set the interrupt mode
userFunction = tempUserFunction;
if (tempMode == CHANGE) {
ACSR &= ~((1<<ACIS1) | (1<<ACIS0)); //interrupt on toggle event
} else if (tempMode == FALLING) {
ACSR &= ~(1<<ACIS0);
ACSR |= (1<<ACIS1);
} else { //default is RISING
ACSR |= ((1<<ACIS1) | (1<<ACIS0));
}
//enable interrupts
ACSR |= (1<<ACIE);
SREG |= (1<<SREG_I);
_interruptEnabled = 1;
}
//disable the interrupt on comparations
void analogComp::disableInterrupt(void) {
if ((!_initialized) || (!_interruptEnabled)) {
return;
}
ACSR &= ~(1<<ACIE); //disable interrupt
_interruptEnabled = 0;
}
//switch off the analog comparator
void analogComp::setOff() {
if (_initialized) {
if (_interruptEnabled) {
ACSR &= ~(1<<ACIE); //disable interrupts on AC events
_interruptEnabled = 0;
}
ACSR |= (1<<ACD); //switch off the AC
//reenable digital buffer on pins AIN0 && AIN1
#if defined(ATTINYx5)
DIDR0 |= ((1<<AIN1D) | (1<<AIN0D));
#elif defined(ATTINYx4)
DIDR0 |= ((1<<ADC2D) | (1<<ADC1D));
#elif defined (ATMEGAx4)
DIDR1 |= (1<<AIN0D);
#elif defined (ATTINYx313)
DIDR |= ((1<<AIN1D) | (1<<AIN0D));
#elif defined (ATMEGAx8) || defined(ATMEGAx4) || defined(ATMEGAx0)
DIDR1 |= ((1<<AIN1D) | (1<<AIN0D));
#endif
#ifndef ATTINYx313
//if ((AC_REGISTER & (1<<ACME)) == 1) { //we must reset the ADC
if (oldADCSRA & (1<<ADEN)) { //ADC has to be powered up
AC_REGISTER |= (1<<ADEN); //ACDSRA = oldADCSRA;
}
#endif
_initialized = 0;
}
}
//wait for a comparation until the function goes in timeout
uint8_t analogComp::waitComp(unsigned long _timeOut) {
//exit if the interrupt is on
if (_interruptEnabled) {
return 0; //error
}
//no timeOut?
if (_timeOut == 0) {
_timeOut = 5000; //5 secs
}
//set up the analog comparator if it isn't
if (!_initialized) {
setOn(AIN0, AIN1);
_initialized = 0;
}
//wait for the comparation
unsigned long _tempMillis = millis() + _timeOut;
do {
if ((ACSR && (1<<ACO)) == 1) { //event raised
return 1;
}
} while ((long)(millis() - _tempMillis) < 0);
//switch off the analog comparator if it was off
if (!_initialized) {
setOff();
}
return 0;
}
//ISR (Interrupt Service Routine) called by the analog comparator when
//the user choose the raise of an interrupt
#if defined(ATMEGAx8) || defined(ATMEGAx0) || defined(ATMEGAx4)
ISR(ANALOG_COMP_vect) {
#else
ISR(ANA_COMP_vect) {
#endif
userFunction(); //call the user function
}
analogComp analogComparator;