-
Notifications
You must be signed in to change notification settings - Fork 46
/
aftb_vpp.h
437 lines (389 loc) · 11 KB
/
aftb_vpp.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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
/*
* Variable voltage functions for Afterburner GAL project.
*
* 2024-02-02 Minor changes in varVppInit()
*/
#ifndef __AFTB_VPP_H__
#define __AFTB_VPP_H__
#include <EEPROM.h>
// ensure mcp4131 pot uses the right pins
#define POT_CS A3
#define POT_CLK A4
#define POT_DAT A5
#define VPP A0
#if CONFIG_IDF_TARGET_ESP32S2 == 1
// ESP32-S2
#include "driver/adc.h"
#define ADC_PIN ADC2_CHANNEL_3
#define EEPROM_BEGIN() EEPROM.begin(128)
#define EEPROM_UPDATE(A,V) if ((V) != EEPROM.read((A))) EEPROM.write((A),(V))
#define EEPROM_END() EEPROM.end()
#else
// AVR
#define EEPROM_BEGIN()
#define EEPROM_UPDATE(A,V) EEPROM.update((A),(V))
#define EEPROM_END()
#endif
#include "aftb_mcp4131.h"
#ifndef FAIL
#define FAIL 0
#define OK 1
#endif
#define ABS(X) ((X) < 0 ? -(X) : (X));
#define VPP_5V0 0xFF
#define VPP_9V0 0
#define VPP_9V5 1
#define VPP_10V0 2
#define VPP_10V5 3
#define VPP_11V0 4
#define VPP_11V5 5
#define VPP_12V0 6
#define VPP_12V5 7
#define VPP_13V0 8
#define VPP_13V5 9
#define VPP_14V0 10
#define VPP_14V5 11
#define VPP_15V0 12
#define VPP_15V5 13
#define VPP_16V0 14
#define VPP_16V5 15
#define MAX_WIPER 16
#define VPP_VERBOSE 0
#ifdef EXTERNAL
#define ANALOG_REF_EXTERNAL EXTERNAL
#else
#define ANALOG_REF_EXTERNAL AR_EXTERNAL
#endif
//UNO R4 Minima or Wifi (Aref internally pulled down by 130kOhm, AVR Uno R3 pulled down by 32kOhm)
#ifdef _RENESAS_RA_
#define AREF_IS_3V2
#endif
//pot wiper indices for the voltages
uint8_t vppWiper[MAX_WIPER] = {0};
// VPP must ramp-up to prevent voltage spikes and possibly resetting arduino
// These values are for mcp4151 (for mcp4131 they are divided by 2)
#define varVppSetMax() varVppSetVppIndex(0x80); \
varVppSetVppIndex(0xE0); \
varVppSetVppIndex(0xF4); \
varVppSetVppIndex(0xFA); \
varVppSetVppIndex(0xFF);
#define varVppSetMin() varVppSetVppIndex(0x0);
uint8_t wiperStat = 0; //enabled / disabled
int8_t calOffset = 0; // VPP calibration offset: value 10 is 0.1V, value -10 is -0.1V
static void varVppReadCalib(void) {
uint8_t i;
EEPROM_BEGIN();
//calibration not found
if (EEPROM.read(0) != 0xAF || EEPROM.read(1) != 0xCA) {
vppWiper[0] = 0;
Serial.println(F("No calibration data in EEPROM"));
EEPROM_END();
return;
}
calOffset = (int8_t) EEPROM.read(2);
for (i = 0; i < MAX_WIPER; i++) {
vppWiper[i] = EEPROM.read(i + 3);
#if 0
Serial.print(F("Calib "));
Serial.print(i);
Serial.print(F(":"));
Serial.println(vppWiper[i]);
#endif
}
EEPROM_END();
}
// internal use only - set the wiper value on the digital pot
static void varVppSetVppIndex(uint8_t value) {
uint8_t i;
#if VPP_VERBOSE
Serial.print(F("varSetVppIndex "));
Serial.println(value);
#endif
mcp4131_write(ADDR_WIPER, value);
#if VPP_PARANOID
i = mcp4131_read(ADDR_WIPER);
if (i != value) {
Serial.print(F("Error writing POT value. Expected:"));
Serial.print(value);
Serial.print(F(" Actual:"));
Serial.println(i);
}
#endif
if (value == 0) {
mcp4131_disableWiper();
wiperStat = 0;
} else if (wiperStat == 0) {
mcp4131_enableWiper();
wiperStat = 1;
}
}
//use by the app code - set the variable voltage
static void varVppSet(uint8_t value) {
uint8_t v;
int8_t inc;
if (value == VPP_5V0 || value >= MAX_WIPER) {
varVppSetVppIndex(0);
return;
}
#if VPP_VERBOSE
Serial.print(F("varSetVpp "));
Serial.print(value);
Serial.print(F(":"));
Serial.println(vppWiper[value]);
#endif
//ramp up to prevent massive voltage overshoots
v = vppWiper[value] / 3;
inc = v >> 2;
while (v < vppWiper[value]) {
varVppSetVppIndex(v);
v+= (inc << 1);
inc -= (inc >> 1);
if (inc < 2) {
inc = 2;
}
}
varVppSetVppIndex(vppWiper[value]);
}
// UNO R4/Minima - Renesas IC (significant ADC gain errors measured)
#ifdef AREF_IS_3V2
#define SAMPLE_CNT 16
#define SAMPLE_DIVIDER 8
#define SAMPLE_MULTIPLIER 25
// SAMPLE_SHIFT moves the ADC gain error up/down
#define SAMPLE_SHIFT -45;
// ESP32-S2 (VREF 2.5V)
#elif CONFIG_IDF_TARGET_ESP32S2 == 1
#define SAMPLE_CNT 18
#define SAMPLE_DIVIDER 10
#define SAMPLE_MULTIPLIER 1
#define SAMPLE_OFFSET 5
//AVR based Arduinos (no ADC gain errors measured)
#else
#define SAMPLE_CNT 14
#define SAMPLE_DIVIDER 8
#define SAMPLE_MULTIPLIER 1
#define SAMPLE_OFFSET 5
#endif
static int16_t varVppMeasureVpp(int8_t printValue) {
int8_t i = 0;
uint16_t r1 = 0;
int16_t r2; //correction for ADC gain error
while (i++ < SAMPLE_CNT) {
r1 += analogRead(VPP);
}
r2 = (r1 / (SAMPLE_DIVIDER * SAMPLE_MULTIPLIER));
#ifdef SAMPLE_OFFSET
r1+= SAMPLE_OFFSET;
#endif
r1 /= SAMPLE_DIVIDER;
#ifdef SAMPLE_SHIFT
r2 += SAMPLE_SHIFT;
r1 += r2;
#endif
r1 += calOffset;
if (printValue) {
uint8_t a = r1%100;
Serial.print(r1/100);
Serial.print(F("."));
if (a < 10) {
Serial.print(F("0"));
}
#if 1
Serial.println(a);
#else
//debug - display the voltage skew value in r2
Serial.print(a);
Serial.println(F(", "));
Serial.println(r2);
#endif
}
return r1;
}
// Returns 1 on Success, 0 on Failure
static uint8_t varVppCalibrateVpp(void) {
uint8_t vppIndex = 0;
uint8_t i = 1;
int16_t v = 900; //starting at 9.00 V
int16_t r1 = 0;
int16_t r2;
int16_t minDif;
Serial.print(F("VPP calib. offset: "));
Serial.println(calOffset);
varVppSetVppIndex(1);
delay(300); //settle voltage
while (1) {
// reset the 'minimal difference' variable every time we search for a new index
minDif = 9000;
//the 'vppWiper' storage for tap indices is 8bit wide, therefore we must not use tap index 256.
while (i <= 0xFF) {
int16_t d1,d2;
varVppSetVppIndex(i);
delay(100); //let the voltage settle
r2 = varVppMeasureVpp(0);
// Sanity check: the previous voltage can't be higher than the voltage just measured
// because we are ramping up the voltage. Therefore it must be an ADC measurement
// glitch. In that case use the previous value as the measured one.
// This can happen at lower tap indices where the voltage differences are very small (like 0.01V).
if (r1 > r2) {
r2 = r1;
}
d1 = r1 - v;
d2 = r2 - v;
d1 = ABS(d1);
d2 = ABS(d2);
#if VPP_VERBOSE
Serial.print(i);
Serial.print(F(") r2="));
Serial.print(r2, DEC);
Serial.print(F(" d1="));
Serial.print(d1, DEC);
Serial.print(F(" d2="));
Serial.print(d2, DEC);
Serial.print(F(" md="));
Serial.println(minDif, DEC);
#endif
if (r2 <= 100) { // less than 1V ? Failure
r1 = FAIL;
goto ret;
}
if (d2 <= minDif) {
minDif = d2;
vppWiper[vppIndex] = i;
//check last value / voltage
if (i == 0xFF) {
if (v >= 1620 && v <= 1670) {
Serial.println(F("*Index for VPP 1650 is 255"));
r1 = OK;
goto ret;
}
r1 = FAIL;
goto ret;
}
} else {
i--;
minDif = 5000;
Serial.print(F("*Index for VPP "));
Serial.print(v);
Serial.print(F(" is "));
Serial.println(i);
break;
}
r1 = r2;
i++;
}
vppIndex++;
#if VPP_VERBOSE
Serial.print(F("vppIndex "));
Serial.println(vppIndex);
#endif
if (vppIndex >= MAX_WIPER) {
r1 = OK;
goto ret;
}
v += 50; //next voltage to search for is 0.5V higher
}
ret:
varVppSet(VPP_5V0);
return r1;
}
static void varVppStoreWiperCalib() {
uint8_t i = 0;
//sanity check
if (vppWiper[0] == 0) {
#ifdef VPP_VERBOSE
Serial.println(F("VPP wiper is 0"));
#endif
return;
}
#ifdef VPP_VERBOSE
Serial.println(F("VPP storing calibration"));
#endif
EEPROM_BEGIN();
//write Afterburner calibration header
EEPROM_UPDATE(0, 0xAF);
EEPROM_UPDATE(1, 0xCA);
EEPROM_UPDATE(2, (uint8_t) calOffset);
while (i < MAX_WIPER) {
EEPROM_UPDATE(3 + i, vppWiper[i]);
i++;
}
EEPROM_END();
}
#if CONFIG_IDF_TARGET_ESP32S2 == 1
static void analogReference(uint8_t ref) {
analogReadResolution(10);
adc2_config_channel_atten(ADC_PIN, ADC_ATTEN_DB_11); // AREF 2.5V
}
#endif
//return 1 on success (variable VPP functionality present), 0 on failure (VPP not detected on board)
static int8_t varVppInit(void) {
analogReference(ANALOG_REF_EXTERNAL); //use 3V3 external reference
analogRead(VPP); // Perform a dummy conversion referring to the datasheet
wiperStat = 0; //wiper disabled
mcp4131_init();
if (mcp4131_detect()) {
#if VPP_VERBOSE
Serial.print(mcp4131_detected ? F("MCP4131") : F("MCP4151"));
Serial.println(F(" POT found"));
#endif
return OK;
} else {
#if VPP_VERBOSE
Serial.println(F("POT not found"));
#endif
return FAIL;
}
}
//return 1 on success (VPP calibration appears correct), 0 on failure
static int8_t varVppCheckCalibration(void) {
int16_t v;
varVppReadCalib();
if (vppWiper[0] == 0) {
Serial.println(F("I: VPP not calibrated"));
return FAIL;
}
#if 0
// This shoots the VPP to 9V - in theory no GALs should have an issue with that voltage.
// Also, the On switch should be turned off, preventing VPP to reach the GAL pins.
// check actual voltage
varVppSet(VPP_9V0);
delay(200); //Settle voltage
v = varVppMeasureVpp(0);
varVppSet(VPP_5V0); //set VPP back to 5V
// lower voltages have a good resolution, so we can have a tight voltage check bounds
if (v < 890 || v > 910) {
Serial.print(F("ER: VPP voltage check of 9V failed. Expected 900, measured "));
Serial.println(v);
return FAIL;
}
#endif
return OK;
}
// only for VPP testing and debugging
static void varrVppTestRamp(void) {
uint8_t i = 1;
while (i < 256) {
varVppSetVppIndex(i);
delay(400);
Serial.println(i, DEC);
varVppMeasureVpp(1);
i++;
}
delay(2000);
varVppSetVppIndex(0);
}
static int8_t varVppCalibrate(void) {
#if 0
// only for testing and debugging
varrVppTestRamp();
return OK;
#endif
if (varVppCalibrateVpp()) {
varVppStoreWiperCalib();
} else {
Serial.println(F("ER: Wiper calibration failed"));
return FAIL;
}
return OK;
}
#endif