-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathAnalogScanner.cpp
292 lines (254 loc) · 7.99 KB
/
AnalogScanner.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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
// AnalogScanner.cpp - Implementation file for the AnalogScanner
// library for Arduino.
//
// Copyright 2014 Mark Rose, [email protected]
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This code is designed to support these Arduino boards and
// Atmel processors:
//
// Processor Boards
// --------- ------
// ATmega328 Uno, Mini, Nano
//
// ATmega32u4 Leonardo, Micro, Esplora
//
// ATmega2560 Mega 2560
//
// As of this writing, it has only been tested by the author on the
// Uno (ATmega328), however.
//
// The information sources for the code in this library were both
// the Arduino IDE library source files, particularly wiring_analog.c
// and pins_arduino.h, and the Atmel reference manuals.
#include <Arduino.h>
#include <AnalogScanner.h>
// Define cbi() and sbi() for clearing and setting bits in the
// ADC registers.
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
// A pointer to the currently active scanner. The ADC interrupt
// handler calls the currently active scanner to process a new
// analog value when available.
AnalogScanner *AnalogScanner::pCurrentScanner = NULL;
// Gets the index (0-15) corresponding to a pin number. If
// the pin number is already an index in the range 0-15, it is
// returned unchanged. Otherwise it is converted to a pin index.
//
// The mapping is A0->0, A1->1, ..., A15->15.
int AnalogScanner::getPinIndex(int pin) {
switch (pin) {
case A0: //FALLTHROUGH
case 0: return 0;
case A1: //FALLTHROUGH
case 1: return 1;
case A2: //FALLTHROUGH
case 2: return 2;
case A3: //FALLTHROUGH
case 3: return 3;
case A4: //FALLTHROUGH
case 4: return 4;
case A5: //FALLTHROUGH
case 5: return 5;
case A6: //FALLTHROUGH
case 6: return 6;
case A7: //FALLTHROUGH
case 7: return 7;
#ifdef A8
case A8: //FALLTHROUGH
case 8: return 8;
#endif
#ifdef A9
case A9: //FALLTHROUGH
case 9: return 9;
#endif
#ifdef A10
case A10: //FALLTHROUGH
case 10: return 10;
#endif
#ifdef A11
case A11: //FALLTHROUGH
case 11: return 11;
#endif
#ifdef A12
case A12: //FALLTHROUGH
case 12: return 12;
#endif
#ifdef A13
case A13: //FALLTHROUGH
case 13: return 13;
#endif
#ifdef A14
case A14: //FALLTHROUGH
case 14: return 14;
#endif
#ifdef A15
case A15: //FALLTHROUGH
case 15: return 15;
#endif
default:
return 0;
}
}
// Gets the pin number for a pin or channel.
// Code from wiring_analog.c, lines 44-57.
uint8_t AnalogScanner::normalizePin(uint8_t pin) {
#if defined(analogPinToChannel)
#if defined(__AVR_ATmega32U4__)
if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#endif
pin = analogPinToChannel(pin);
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if (pin >= 54) pin -= 54; // allow for channel or pin numbers
#elif defined(__AVR_ATmega32U4__)
if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__)
if (pin >= 24) pin -= 24; // allow for channel or pin numbers
#elif defined(ADAFRUIT_FEATHER_M0)
pin = g_APinDescription[pin].ulADCChannelNumber;
#else
if (pin >= 14) pin -= 14; // allow for channel or pin numbers
#endif
return pin;
}
// Creates a new instance of the analog input scanner. Initializes
// all scanned values to zero and all callbacks to null. Clears the
// scan order and sets the analog reference voltage to the default.
AnalogScanner::AnalogScanner() {
for (int i=0; i < ANALOG_INPUTS; ++i) {
values[i] = 0;
pCallback[i] = NULL;
}
scanOrderSize = 0;
currentIndex = -1;
analogRef = DEFAULT;
}
// Sets a callback function for a particular analog pin. The
// function is called by the interrupt handling code as soon
// as a new analog value is available.
void AnalogScanner::setCallback(int pin, void (*p)(int index, int pin, int value)) {
pCallback[getPinIndex(pin)] = p;
}
// Sets the scan order for the analog pins. The same pin may
// be specified multiple times if a pin should be sampled more
// often.
void AnalogScanner::setScanOrder(int n, const int order[]) {
scanOrderSize = min(SCAN_ORDER_MAX, n);
for (int i=0; i < scanOrderSize; ++i) {
requestedPins[i] = order[i];
normalizedPins[i] = normalizePin(order[i]);
}
}
// Gets the most recently read value for an analog pin.
int AnalogScanner::getValue(int pin) {
noInterrupts();
int value = values[getPinIndex(pin)];
interrupts();
return value;
}
// Sets the analog reference voltage. See the built-in Arduino
// function analogReference() for details.
void AnalogScanner::setAnalogReference(int reference) {
analogRef = reference & 3;
}
// Begins scanning the analog input pins.
void AnalogScanner::beginScanning() {
pCurrentScanner = this;
#if defined(__SAMD21G18A__)
ADC->CTRLA.bit.ENABLE = 1;
delay(1);
ADC->INTENSET.bit.RESRDY = 1;
NVIC_EnableIRQ(ADC_IRQn); // enable ADC interrupts
NVIC_SetPriority(ADC_IRQn, 2); //set priority of the interrupt
#else
sbi(ADCSRA, ADEN); // Enable the ADC.
delay(1);
cbi(ADMUX, ADLAR); // Make sure the ADC value is right-justified.
sbi(ADCSRA, ADIE); // Enable ADC complete interrupts.
#endif
startNextScan();
}
// Ends scanning the analog input pins. Disables the ADC to
// save power.
void AnalogScanner::endScanning() {
#if defined(__SAMD21G18A__)
ADC->CTRLA.bit.ENABLE = 0;
ADC->INTENSET.bit.RESRDY = 0;
#else
cbi(ADCSRA, ADEN); // Disable the ADC.
cbi(ADCSRA, ADIE); // Disable ADC complete interrupts.
#endif
}
// Starts the next ADC read.
void AnalogScanner::startNextScan() {
if (scanOrderSize > 0) {
if (++currentIndex >= scanOrderSize) {
currentIndex = 0;
}
currentPin = requestedPins[currentIndex];
int pin = normalizedPins[currentIndex];
#if defined(__SAMD21G18A__)
ADC->INPUTCTRL.bit.MUXPOS = pin;
ADC->SWTRIG.bit.START = 1;
#else
#ifdef MUX5
// Set whether we're reading from inputs 0-7 or 8-15.
ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif
ADMUX = (analogRef << 6) | (pin & 7);
sbi(ADCSRA, ADSC); // Start the ADC conversion.
#endif
}
}
// Processes a new value from the ADC.
void AnalogScanner::processScan() {
// We must read ADCL first, which locks ADCH until it is read.
#if defined(__SAMD21G18A__)
int index = getPinIndex(currentPin);
int pin = currentPin;
values[index] = ADC->RESULT.reg;
#else
int low = ADCL;
int high = ADCH;
int index = getPinIndex(currentPin);
int pin = currentPin;
values[index] = (high << 8) | low;
#endif
int currentIndexLocal = currentIndex; //make a local copy of the currentIndex
// Invoke the next scan before the callback, to make the
// read rate more uniform.
startNextScan(); //currentIndex will be updated/wrapped to 0 here
if (pCallback[index] != NULL) {
pCallback[index](currentIndexLocal, pin, values[index]); //use the not updated copy of the currentIndex here
}
}
// Handles the ADC completion interrupt by requesting that the
// current scanner read teh newly available analog input value.
void AnalogScanner::scanComplete() {
pCurrentScanner->processScan();
}
// Defines an ADC interrupt processing routine that asks the
// currently active scanner to process a newly scanned value.
#if defined (__SAMD21G18A__)
void ADC_Handler() {
AnalogScanner::scanComplete();
}
#else
ISR(ADC_vect) {
AnalogScanner::scanComplete();
}
#endif