-
Notifications
You must be signed in to change notification settings - Fork 4
/
ATSAMD21_ADC.h
299 lines (245 loc) · 9.6 KB
/
ATSAMD21_ADC.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
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
293
294
295
296
297
298
299
// Blake Felt
// ATSAMD21_ADC.h
// Adds some extra functions to the ADC, such as
// 16 bit and differential mode
#ifndef ATSAMD21_ADC_H
#define ATSAMD21_ADC_H
#ifdef __cplusplus
extern "C" {
#endif // ifdef __cplusplus
#ifndef ADC_CTRLB_RESSEL_12BIT_Val
#define ADC_CTRLB_RESSEL_8BIT_Val 0x03
#define ADC_CTRLB_RESSEL_10BIT_Val 0x02 // default by Arduino
#define ADC_CTRLB_RESSEL_12BIT_Val 0x00
#endif
#define ADC_CTRLB_RESSEL_16BIT_Val 0x01 // used for averaging mode output
#define ADC_PIN_TEMP 0x18 // positive mux, pg 870
#define ADC_PIN_BANDGAP 0x19
#define ADC_PIN_SCALEDCOREVCC 0x1A
#define ADC_PIN_SCALEDIOVCC 0x1B
#define ADC_PIN_DAC 0x1C
#define ADC_PIN_GND 0x18 // negative mux, pg 869
#define ADC_PIN_IOGND 0x19
#define ADC_GAIN_1 0x00 // pg 868
#define ADC_GAIN_2 0x01
#define ADC_GAIN_4 0x02
#define ADC_GAIN_8 0x03
#define ADC_GAIN_16 0x04
#define ADC_GAIN1_DIV2 0x0F // default by Arduino
#define ADC_REF_INT1V 0x00 // 1.0V reference, pg 861
#define ADC_REF_INTVCC0 0x01 // 1/1.48 VDDANA
#define ADC_REF_INTVCC1 0x02 // 1/2 VDDANA (only for VDDANA > 2.0V) // default
#define ADC_REF_VREFA 0x03 // external reference
#define ADC_REF_VREFB 0x04 // external reference
#define ADC_PRESCALER_DIV4 0x00 // pg 864
#define ADC_PRESCALER_DIV8 0x01
#define ADC_PRESCALER_DIV16 0x02
#define ADC_PRESCALER_DIV32 0x03
#define ADC_PRESCALER_DIV64 0x04
#define ADC_PRESCALER_DIV128 0x05
#define ADC_PRESCALER_DIV256 0x06
#define ADC_PRESCALER_DIV512 0x07 // Arduino default
// NVM Software Calibration Area Mapping, pg 32. Address starting at NVMCTRL_OTP4.
// NVM register access code modified from https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/USB/samd21_host.c
// ADC Linearity Calibration value. Should be written to the CALIB register.
#define NVM_ADC_LINEARITY_POS 27
#define NVM_ADC_LINEARITY_SIZE 8
// ADC Bias Calibration value. Should be written to the CALIB register.
#define NVM_ADC_BIASCAL_POS 35
#define NVM_ADC_BIASCAL_SIZE 3
// Taken from Arduino IDE:
// Wait for synchronization of registers between the clock domains
static __inline__ void syncADC() __attribute__((always_inline, unused));
static void syncADC() {
while(ADC->STATUS.bit.SYNCBUSY == 1);
}
// Taken from Arduino IDE:
// Wait for synchronization of registers between the clock domains
static __inline__ void syncDAC() __attribute__((always_inline, unused));
static void syncDAC() {
while (DAC->STATUS.bit.SYNCBUSY == 1);
}
// taken from Arduino IDE, changes the pin to an input:
int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral );
uint8_t analogReadExtended(uint8_t bits) {
/*
* Allows for adc to read 8, 10, or 12 bits normally or 13-16 bits using oversampling and decimation.
* See pages 853 & 862
* 8,10,12 bit = 1 sample ~ 436 microseconds
* 13 bit = 4 samples ~ 1668 microseconds
* 14 bit = 16 samples ~ 6595 microseconds
* 15 bit = 64 samples ~ 26308 microseconds
* 16 bit = 256 samples ~ 105156 microseconds
*/
switch(bits) {
case 8:
ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_8BIT_Val;
ADC->AVGCTRL.bit.ADJRES = 0x0;
ADC->AVGCTRL.bit.SAMPLENUM = 0x0;
return 0;
break;
case 10:
ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val;
ADC->AVGCTRL.bit.ADJRES = 0x0;
ADC->AVGCTRL.bit.SAMPLENUM = 0x0;
return 0;
break;
case 12:
ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_12BIT_Val;
ADC->AVGCTRL.bit.ADJRES = 0x0;
ADC->AVGCTRL.bit.SAMPLENUM = 0x0;
return 0;
break;
case 13:
ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val;
ADC->AVGCTRL.bit.ADJRES = 0x1;
ADC->AVGCTRL.bit.SAMPLENUM = 0x2;
return 0;
break;
case 14:
ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val;
ADC->AVGCTRL.bit.ADJRES = 0x2;
ADC->AVGCTRL.bit.SAMPLENUM = 0x4;
return 0;
break;
case 15:
ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val;
ADC->AVGCTRL.bit.ADJRES = 0x1;
ADC->AVGCTRL.bit.SAMPLENUM = 0x6;
return 0;
break;
case 16:
ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val;
ADC->AVGCTRL.bit.ADJRES = 0x0;
ADC->AVGCTRL.bit.SAMPLENUM = 0x8;
return 0;
break;
default:
return -1;
break;
}
}
// returns the internal pin value of the specified pin, useful
// for analogDifferentialRaw function
uint8_t internalPinValue(uint8_t pin) {
return g_APinDescription[pin].ulADCChannelNumber;
}
// modified from Arduino analogRead, can be used in conjunction with analogRead:
int16_t analogDifferential(uint8_t pin_pos,uint8_t pin_neg) {
if(pin_pos<A0) pin_pos += A0;
if(pin_neg<A0) pin_neg += A0;
if((g_APinDescription[pin_neg].ulADCChannelNumber>0x07) && (pin_neg<ADC_PIN_GND)) { // if the negative pin is out of bounds
return 0;
}
uint32_t value_read = 0;
pinPeripheral(pin_pos,PIO_ANALOG); // set pins to analog mode
pinPeripheral(pin_neg,PIO_ANALOG);
if((pin_pos == A0) || (pin_neg == A0)) { // Disable DAC
syncDAC();
DAC->CTRLA.bit.ENABLE = 0x00; // Disable DAC
syncDAC();
}
syncADC();
ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[pin_pos].ulADCChannelNumber; // Selection for the positive ADC input
ADC->INPUTCTRL.bit.MUXNEG = g_APinDescription[pin_neg].ulADCChannelNumber; // negative ADC input
syncADC();
ADC->CTRLA.bit.ENABLE = 0x01; // enable adc
ADC->CTRLB.bit.DIFFMODE = 1; // set to differential mode
syncADC();
ADC->SWTRIG.bit.START = 1; // start conversion
ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; // clear the data ready flag
syncADC();
ADC->SWTRIG.bit.START = 1; // restart conversion, as changing inputs messes up first conversion
while(ADC->INTFLAG.bit.RESRDY == 0); // Wait for conversion to complete
value_read = ADC->RESULT.reg; // read the value
syncADC();
ADC->CTRLA.bit.ENABLE = 0x00; // disable adc
ADC->CTRLB.bit.DIFFMODE = 0; // put back into single-ended mode
ADC->INPUTCTRL.bit.MUXNEG = ADC_PIN_GND; // set back muxneg to internal ground
syncADC();
return value_read;
}
// same as the above function, but no error checking, no pin types are changed, and the positive and negative
// inputs are the raw values being input. The DAC is not automatically shut off either. See datasheet page
int16_t analogDifferentialRaw(uint8_t mux_pos,uint8_t mux_neg) {
uint32_t value_read = 0;
syncADC();
ADC->INPUTCTRL.bit.MUXPOS = mux_pos; // Selection for the positive ADC input
ADC->INPUTCTRL.bit.MUXNEG = mux_neg; // negative ADC input
syncADC();
ADC->CTRLA.bit.ENABLE = 0x01; // enable adc
ADC->CTRLB.bit.DIFFMODE = 1; // set to differential mode
syncADC();
ADC->SWTRIG.bit.START = 1; // start conversion
ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; // clear the data ready flag
syncADC();
ADC->SWTRIG.bit.START = 1; // restart conversion, as changing inputs messes up first conversion
while(ADC->INTFLAG.bit.RESRDY == 0); // Wait for conversion to complete
value_read = ADC->RESULT.reg; // read the value
syncADC();
ADC->CTRLA.bit.ENABLE = 0x00; // disable adc
ADC->CTRLB.bit.DIFFMODE = 0; // put back into single-ended mode
ADC->INPUTCTRL.bit.MUXNEG = ADC_PIN_GND; // set back muxneg to internal ground
syncADC();
return value_read;
}
// sets the gain of the ADC. See page 868. All values defined above.
void analogGain(uint8_t gain) {
syncADC();
ADC->INPUTCTRL.bit.GAIN = gain;
syncADC();
}
// calibrates the bias and linearity based on the nvm register.
// NVM register access code modified from https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/USB/samd21_host.c
// datasheet pages 32 and 882
void analogCalibrate() {
syncADC();
// read NVM register
uint32_t adc_linearity = (*((uint32_t *)(NVMCTRL_OTP4) // original position
+ (NVM_ADC_LINEARITY_POS / 32)) // move to the correct 32 bit window, read value
>> (NVM_ADC_LINEARITY_POS % 32)) // shift value to match the desired position
& ((1 << NVM_ADC_LINEARITY_SIZE) - 1); // apply a bitmask for the desired size
uint32_t adc_biascal = (*((uint32_t *)(NVMCTRL_OTP4)
+ (NVM_ADC_BIASCAL_POS / 32))
>> (NVM_ADC_BIASCAL_POS % 32))
& ((1 << NVM_ADC_BIASCAL_SIZE) - 1);
// write values to CALIB register
ADC->CALIB.bit.LINEARITY_CAL = adc_linearity;
ADC->CALIB.bit.BIAS_CAL = adc_biascal;
syncADC();
}
// set the analog reference voltage, but with all available options
// (the Arduino IDE neglects some). The Arduino IDE also changes
// the gain when analogReference() is used, but this won't. pg 861
void analogReference2(uint8_t ref) {
syncADC();
ADC->REFCTRL.bit.REFSEL = ref;
syncADC();
}
// increases accuracy of gain stage by enabling the reference buffer
// offset compensation. Takes longer to start. pg 861
void analogReferenceCompensation(uint8_t val) {
if(val>0) val = 1;
syncADC();
ADC->REFCTRL.bit.REFCOMP = val;
syncADC();
}
// sets the ADC clock relative to the peripheral clock. pg 864
void analogPrescaler(uint8_t val) {
syncADC();
ADC->CTRLB.bit.PRESCALER = val;
syncADC();
}
// resets the ADC. pg 860
// note that this doesn't put back the default values set by the
// Arduino IDE.
void analogReset() {
syncADC();
ADC->CTRLA.bit.SWRST = 1; // set reset bit
while(ADC->CTRLA.bit.SWRST==1); // wait until it's finished
syncADC();
}
#ifdef __cplusplus
}
#endif // ifdef __cplusplus
#endif // ifndef ATSAMD21_ADC_H