/* Test suite for the ADC library * Do not connect any pin of the board to anything while it's running * It uses the internal pulldown and pullup resistors to expose the pins to VDD or GND. */ #include #include ADC *adc = new ADC(); // adc object #if defined(ADC_TEENSY_LC) // teensy LC #define PINS 13 #define DIG_ADC_0_PINS 13 #define DIG_ADC_1_PINS 0 #define PINS_DIFF 2 uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12}; uint8_t adc_pins_dig_ADC_0[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12}; uint8_t adc_pins_dig_ADC_1[] = {}; uint8_t adc_pins_diff[] = {A10, A11}; #elif defined(ADC_TEENSY_3_0) // teensy 3.0 #define PINS 14 #define DIG_ADC_0_PINS 10 #define DIG_ADC_1_PINS 0 #define PINS_DIFF 4 uint8_t adc_pins[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13}; uint8_t adc_pins_dig_ADC_0[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9}; uint8_t adc_pins_dig_ADC_1[] = {}; uint8_t adc_pins_diff[] = {A10, A11, A12, A13}; #elif defined(ADC_TEENSY_3_1) || defined(ADC_TEENSY_3_2) // teensy 3.1/3.2 #define PINS 21 #define DIG_ADC_0_PINS 10 #define DIG_ADC_1_PINS 8 #define PINS_DIFF 4 uint8_t adc_pins_dig_ADC_0[] = { A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, }; uint8_t adc_pins_dig_ADC_1[] = {A2, A3, A15, A16, A17, A18, A19, A20}; uint8_t adc_pins_diff[] = {A10, A11, A12, A13}; #elif defined(ADC_TEENSY_3_5) // Teensy 3.5 #define PINS 27 #define DIG_ADC_0_PINS 12 #define DIG_ADC_1_PINS 11 #define PINS_DIFF 2 uint8_t adc_pins_dig_ADC_0[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A14, A15}; uint8_t adc_pins_dig_ADC_1[] = {A2, A3, A12, A13, A16, A17, A18, A19, A20, A23, A24}; uint8_t adc_pins_diff[] = {A10, A11}; #elif defined(ADC_TEENSY_3_6) // Teensy 3.6 #define PINS 25 #define DIG_ADC_0_PINS 12 #define DIG_ADC_1_PINS 11 #define PINS_DIFF 2 uint8_t adc_pins_dig_ADC_0[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A14, A15}; uint8_t adc_pins_dig_ADC_1[] = {A2, A3, A12, A13, A16, A17, A18, A19, A20, A23, A24}; uint8_t adc_pins_diff[] = {A10, A11}; #elif defined(ADC_TEENSY_4) // Teensy 4 #define PINS 14 #define DIG_ADC_0_PINS 10 #define DIG_ADC_1_PINS 10 #define PINS_DIFF 0 uint8_t adc_pins_dig_ADC_0[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9}; uint8_t adc_pins_dig_ADC_1[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9}; uint8_t adc_pins_diff[] = {}; #elif defined(ADC_TEENSY_4_1) // Teensy 4.1 #define PINS 18 #define DIG_ADC_0_PINS 10 #define DIG_ADC_1_PINS 10 #define PINS_DIFF 0 uint8_t adc_pins_dig_ADC_0[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9}; uint8_t adc_pins_dig_ADC_1[] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9}; uint8_t adc_pins_diff[] = {}; #endif // defined long int value = 0; #ifdef ADC_TEENSY_4 const uint32_t num_samples = 10000; #else const uint32_t num_samples = 100; #endif // test the analog pins that also have a digital function // Teensy LC has 20-50 kOhm pullup, no idea about pulldown (do they exist?) // Teensy 3.0 has 22-50 kOhm pullup and pulldown // Teensy 3.1/3.2 has 20-50 kOhm pullup and pulldown // Teensy 3.5 has 20-50 kOhm pullup and pulldown // Teensy 3.6 has 20-50 kOhm pullup and pulldown // Teensy 4 has 100 kOhm pulldow, and 22 kOhm pullup, so pulldown will fail faster bool test_pullup_down(bool pullup, bool debug = false) { uint8_t mode = pullup ? INPUT_PULLUP : INPUT_PULLDOWN; const int max_val = adc->adc0->getMaxValue(); bool pass_test = true; for (auto pin : adc_pins_dig_ADC_0) { pinMode(pin, mode); delay(10); // settle time value = adc->adc0->analogRead(pin); bool fail_condition = pullup ? (value < 0.95 * max_val) : (value > 0.05 * max_val); if (fail_condition) { if (debug) { Serial.print("Pin "); Serial.print(pin); Serial.print(": "); Serial.print("FAILED "); Serial.print(pullup ? "INPUT_PULLUP" : "INPUT_PULLDOWN"); Serial.print("("); Serial.print(value); Serial.print(")"); Serial.print("max_val: "); Serial.print(pullup ? (0.95 * max_val) : (0.05 * max_val)); Serial.println(". "); } pass_test = false; } } #ifdef ADC_DUAL_ADCS for (auto pin : adc_pins_dig_ADC_1) { pinMode(pin, mode); delay(10); // settle time value = adc->adc1->analogRead(pin); bool fail_condition = pullup ? (value < 0.95 * max_val) : (value > 0.05 * max_val); if (fail_condition) { if (debug) { Serial.print("Pin "); Serial.print(pin); Serial.print(": "); Serial.print("FAILED "); Serial.print(pullup ? "INPUT_PULLUP" : "INPUT_PULLDOWN"); Serial.print("("); Serial.print(value); Serial.print(")"); Serial.print("max_val: "); Serial.print(pullup ? (0.95 * max_val) : (0.05 * max_val)); Serial.println(". "); } pass_test = false; } } #endif return pass_test; } const uint8_t pin_cmp = A0; bool test_compare(bool debug = false) { bool pass_test = true; // measurement will be ready if value < 1.0V adc->adc0->enableCompare((int)(1.0 / 3.3 * adc->adc0->getMaxValue()), 0); pinMode(pin_cmp, INPUT_PULLUP); // set to max delay(10); // this should fail value = adc->adc0->analogRead(pin_cmp); if (adc->adc0->fail_flag != ADC_ERROR::COMPARISON) { if (debug) { Serial.println("Comparison didn't fail."); Serial.println(getStringADCError(adc->adc0->fail_flag)); } pass_test = false; } adc->adc0->resetError(); pinMode(pin_cmp, INPUT_PULLDOWN); // set to min delay(50); // this should be ok value = adc->adc0->analogRead(pin_cmp); if (adc->adc0->fail_flag == ADC_ERROR::COMPARISON) { if (debug) { Serial.println("Comparison didn't succeed."); Serial.println(getStringADCError(adc->adc0->fail_flag)); Serial.println(value); } pass_test = false; } adc->adc0->resetError(); adc->adc0->disableCompare(); return pass_test; } bool test_compare_range(bool debug = false) { bool pass_test = true; // ready if value lies out of [1.0,2.0] V adc->adc0->enableCompareRange(1.0 * adc->adc0->getMaxValue() / 3.3, 2.0 * adc->adc0->getMaxValue() / 3.3, 0, 1); pinMode(pin_cmp, INPUT_PULLUP); // set to max // this should be ok value = adc->adc0->analogRead(pin_cmp); if (adc->adc0->fail_flag != ADC_ERROR::CLEAR) { if (debug) { Serial.println("Some other error happened when comparison should have succeeded."); Serial.println(getStringADCError(adc->adc0->fail_flag)); } pass_test = false; } pinMode(pin_cmp, INPUT_PULLDOWN); // set to min adc->adc0->resetError(); // this should be ok value = adc->adc0->analogRead(pin_cmp); if (adc->adc0->fail_flag != ADC_ERROR::CLEAR) { if (debug) { Serial.println("Some other error happened when comparison should have succeeded."); Serial.println(getStringADCError(adc->adc0->fail_flag)); } pass_test = false; } adc->adc0->resetError(); adc->adc0->disableCompare(); return pass_test; } bool test_averages(bool debug = false) { elapsedMicros timeElapsed; bool pass_test = true; const uint8_t averages[] = {4, 8, 16, 32}; float avg_times[4]; volatile int value = 0; adc->adc0->setAveraging(1); timeElapsed = 0; for (uint32_t i = 0; i < num_samples; i++) { value += adc->adc0->analogRead(A0); } float one_avg_time = (float)timeElapsed / num_samples; // Serial.print("1: "); Serial.println(one_avg_time); // Serial.print("value: "); Serial.println(value/num_samples); for (uint8_t i = 0; i < 4; i++) { adc->adc0->setAveraging(averages[i]); timeElapsed = 0; value = 0; for (uint32_t j = 0; j < num_samples; j++) { value += adc->adc0->analogRead(A0); } float time = (float)timeElapsed / num_samples; avg_times[i] = time; // Serial.print(averages[i]); Serial.print(": "); Serial.print(time); // Serial.print(", "); Serial.println(time/one_avg_time); // Serial.print("value: "); Serial.println(value/num_samples); } // the 4 averages is not 4 times as long as the 1 average because // the first sample always takes longer, therefore the 4 avgs take about 3.5 times the first. // 8 avgs take twice as long as 4 and so on. // this is even worse for Teensy LC, where 4 avgs take about 2.6 more than none. if ((avg_times[0] < 2 * one_avg_time) || (avg_times[0] > 4 * one_avg_time)) { pass_test = false; if (debug) { Serial.print("4 averages should take about 4 times longer than one, but it took "); Serial.print(avg_times[0] / one_avg_time); Serial.println(" times longer"); } } // check that the times are between 80% and 120% their theoretical value. for (uint8_t i = 1; i < 4; i++) { if ((avg_times[i] < 2 * 0.8 * avg_times[i - 1]) || (avg_times[i] > 2 * 1.2 * avg_times[i - 1])) { pass_test = false; if (debug) { Serial.print(averages[i]); Serial.print(" averages should take about twice as long as "); Serial.print(averages[i - 1]); Serial.print(", but it took "); Serial.print(avg_times[i] / avg_times[i - 1]); Serial.println(" times longer."); } } } return pass_test; } bool test_all_combinations(bool debug = false) { bool pass_test = true; for (auto average : averages_list) { adc->adc0->setAveraging(average); // set number of averages adc->adc0->setAveraging(average); // set number of averages for (auto resolution : resolutions_list) { adc->adc0->setResolution(resolution); // set bits of resolution adc->adc0->setResolution(resolution); // set bits of resolution for (auto conv_speed : conversion_speed_list) { adc->adc0->setConversionSpeed(conv_speed); // change the conversion speed adc->adc0->setConversionSpeed(conv_speed); // change the conversion speed for (auto samp_speed : sampling_speed_list) { adc->adc0->setSamplingSpeed(samp_speed); // change the sampling speed adc->adc0->setSamplingSpeed(samp_speed); // change the sampling speed adc->adc0->wait_for_cal(); #ifdef ADC_DUAL_ADCS adc->adc1->wait_for_cal(); #endif bool test = test_pullup_down(true, false); // pullups have lower impedance for T4 if (!test) { if (debug) { Serial.print("Average: "); Serial.print(average); Serial.print(", Resolution: "); Serial.print(resolution); Serial.print(", Conversion speed: "); Serial.print(getConversionEnumStr(conv_speed)); Serial.print(", Sampling speed: "); Serial.print(getSamplingEnumStr(samp_speed)); Serial.println(". PULLUP FAILED."); } // VERY_HIGH_SPEED are not guaranteed to work if ((conv_speed != ADC_CONVERSION_SPEED::VERY_HIGH_SPEED) || (samp_speed != ADC_SAMPLING_SPEED::VERY_HIGH_SPEED)) { pass_test = false; } } digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN)); } } } } return pass_test; } void resetSettings() { adc->adc0->setAveraging(16); // set number of averages adc->adc0->setResolution(12); // set bits of resolution adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); // change the conversion speed adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); // change the sampling speed #ifdef ADC_DUAL_ADCS adc->adc1->setAveraging(16); // set number of averages adc->adc1->setResolution(12); // set bits of resolution adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); // change the conversion speed adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); // change the sampling speed adc->adc1->recalibrate(); #endif } void setup() { pinMode(LED_BUILTIN, OUTPUT); Serial.begin(9600); ///// ADC0 //// // reference can be ADC_REFERENCE::REF_3V3, ADC_REFERENCE::REF_1V2 (not for Teensy LC) or ADC_REFERENCE::REF_EXT. //adc->setReference(ADC_REFERENCE::REF_1V2, ADC_0); // change all 3.3 to 1.2 if you change the reference to 1V2 adc->adc0->setAveraging(16); // set number of averages adc->adc0->setResolution(12); // set bits of resolution adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); // change the conversion speed adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); // change the sampling speed adc->adc0->recalibrate(); ////// ADC1 ///// #ifdef ADC_DUAL_ADCS adc->adc1->setAveraging(16); // set number of averages adc->adc1->setResolution(12); // set bits of resolution adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED); // change the conversion speed adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::MED_SPEED); // change the sampling speed adc->adc1->recalibrate(); #endif delay(2000); ////// START TESTS ///////////// // do the tests bool pullup_test = test_pullup_down(true, false); resetSettings(); Serial.print("PULLUP TEST "); Serial.println(pullup_test ? "PASS" : "FAIL"); bool pulldown_test = test_pullup_down(false, false); resetSettings(); Serial.print("PULLDOWN TEST "); Serial.println(pulldown_test ? "PASS" : "FAIL"); bool compare_test = test_compare(true); resetSettings(); Serial.print("COMPARE TEST "); Serial.println(compare_test ? "PASS" : "FAIL"); bool compare_range_test = test_compare_range(true); resetSettings(); Serial.print("COMPARE RANGE TEST "); Serial.println(compare_range_test ? "PASS" : "FAIL"); bool averages_test = test_averages(true); resetSettings(); Serial.print("AVERAGES TEST "); Serial.println(averages_test ? "PASS" : "FAIL"); bool all_combinations_test = test_all_combinations(true); resetSettings(); Serial.print("ALL COMBINATIONS TEST "); Serial.println(all_combinations_test ? "PASS" : "FAIL"); if (pullup_test & pulldown_test & compare_test & compare_range_test & averages_test & all_combinations_test) { Serial.println("ALL TEST PASSED."); } } void loop() { // Print errors, if any. if (adc->adc0->fail_flag != ADC_ERROR::CLEAR) { Serial.print("ADC0: "); Serial.println(getStringADCError(adc->adc0->fail_flag)); } #ifdef ADC_DUAL_ADCS if (adc->adc1->fail_flag != ADC_ERROR::CLEAR) { Serial.print("ADC1: "); Serial.println(getStringADCError(adc->adc1->fail_flag)); } #endif adc->resetError(); digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN)); delay(500); }