diff --git a/lambda-test/avrjunit.c b/lambda-test/avrjunit.c index b7f9dfd..e7d2e36 100644 --- a/lambda-test/avrjunit.c +++ b/lambda-test/avrjunit.c @@ -11,10 +11,11 @@ */ #include +#include #include "USART.h" #include "avrjunit.h" -void runTests(char* suite, test tests[], uint16_t count) { +void runTests(char* const suite, test const tests[], uint16_t const count) { printString("\n"); char tsbuf[128]; snprintf(tsbuf, sizeof(tsbuf), @@ -23,12 +24,17 @@ printString(tsbuf); for (uint16_t i = 0; i < count; i++) { - int result = (*tests[i].test)(); + char cbuf[24]; + char nbuf[64]; char tcbuf[128]; + // TODO use strncat (macro?) + strcpy_P(cbuf, (const char*)pgm_read_word(&(tests[i].class))); + strcpy_P(nbuf, (const char*)pgm_read_word(&(tests[i].name))); snprintf(tcbuf, sizeof(tcbuf), - "\n", - tests[i].class, tests[i].name); + "\n", cbuf, nbuf); printString(tcbuf); + fptr function = (fptr)pgm_read_word(&tests[i].function); + bool result = function(); if (! result) { // failure printString("failed\n"); diff --git a/lambda-test/avrjunit.h b/lambda-test/avrjunit.h index 4835a52..e9f6d78 100644 --- a/lambda-test/avrjunit.h +++ b/lambda-test/avrjunit.h @@ -10,6 +10,7 @@ */ #include +#include #ifndef AVRJUNIT_H_ #define AVRJUNIT_H_ @@ -25,13 +26,19 @@ #define assertFalse(exp) if (exp) return false /** - * A test case with its class, name and pointer to the test function, + * Function pointer for test functions taking no parameters and returning + * true on success and false on failure. + */ +typedef bool (*fptr)(void); + +/** + * A test case with its class, name and test function pointer, * which should return true on success and false on failure. */ -typedef struct { - char* class; - char* name; - bool (*test)(void); +typedef struct PROGMEM { + const char* class; + const char* name; + fptr function; } test; /** @@ -42,6 +49,6 @@ * on the receiving side with a command like: * (stty sane; cat > tests.xml) < /dev/ttyUSB0 */ -void runTests(char* suite, test tests[], uint16_t count); +void runTests(char* const suite, test const tests[], uint16_t const count); #endif /* AVRJUNIT_H_ */ diff --git a/lambda-test/lambda-test.c b/lambda-test/lambda-test.c index ad0c6e4..ca8bb94 100644 --- a/lambda-test/lambda-test.c +++ b/lambda-test/lambda-test.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include "USART.h" @@ -96,31 +97,14 @@ /* Module display */ -// TODO assertions bool testCycle(void) { + // extern uint8_t position; + + assertTrue(getPosition() == 0); cycleDisplay(); - - return true; -} - -// TODO assertions -bool testUpdateInitial(void) { - - return true; -} - -// TODO assertions -bool testUpdate(void) { - measurement meas = {0, 0, 0}; - updateMeas(meas); - - return true; -} - -// TODO test display() with no display connected? -bool testDisplay(void) { - measurement meas = {0, 0, 0}; - displayMeas(meas, " "); + assertTrue(getPosition() == 1); + cycleDisplay(); + assertTrue(getPosition() == 0); return true; } @@ -280,6 +264,17 @@ return true; } +bool testReadMeas(void) { + char* fields[] = {"1", "2", "3"}; + + measurement meas = readMeas(fields); + assertTrue(meas.tempI == 1); + assertTrue(meas.tempO == 2); + assertTrue(meas.lambda == 3); + + return true; +} + bool testToLambdaValue(void) { int16_t lambda = toLambda(132); @@ -391,46 +386,84 @@ return true; } +const char adc_P[] PROGMEM = "adc"; +const char command_P[] PROGMEM = "command"; +const char display_P[] PROGMEM = "display"; +const char integers_P[] PROGMEM = "integers"; +const char interrupts_P[] PROGMEM = "interrupts"; +const char sensors_P[] PROGMEM = "sensors"; +const char strings_P[] PROGMEM = "strings"; -// TODO these long function names passed along as strings use a lot of memory -// use PROGMEM? -test tests[] = { - {"adc", "testSetupADC", testSetupADC}, - {"adc", "testGetVoltage", testGetVoltage}, - {"command", "testIsSimulation", testIsSimulation}, - {"command", "testIsLogging", testIsLogging}, - {"display", "testCycle", testCycle}, - {"display", "testUpdateInitial", testUpdateInitial}, - {"display", "testUpdate", testUpdate}, - {"display", "testDisplay", testDisplay}, - {"integers", "testDivRoundNearest", testDivRoundNearest}, - {"integers", "testDivRoundNearestNumNeg", testDivRoundNearestNumNeg}, - {"integers", "testDivRoundNearestDenNeg", testDivRoundNearestDenNeg}, - {"integers", "testDivRoundNearestBothNeg", testDivRoundNearestBothNeg}, - {"integers", "testDivRoundUp", testDivRoundUp}, - {"integers", "testDivRoundUpNumNeg", testDivRoundUpNumNeg}, - {"integers", "testDivRoundUpDenNeg", testDivRoundUpDenNeg}, - {"integers", "testDivRoundUpBothNeg", testDivRoundUpBothNeg}, - {"interrupts", "testSetupPorts", testSetupPorts}, - {"interrupts", "testSetupSleepMode", testSetupSleepMode}, - {"interrupts", "testInitInterrupts", testInitInterrupts}, - {"interrupts", "testInitTimers", testInitInterrupts}, - {"sensors", "testMeasure", testMeasure}, - {"sensors", "testToLambdaValue", testToLambdaValue}, - {"sensors", "testToLambdaInter", testToLambdaInter}, - {"sensors", "testToTempI", testToTempI}, - {"sensors", "testToTempOValue", testToTempOValue}, - {"sensors", "testToTempOInter", testToTempOInter}, - {"sensors", "testLookupLinInterValue", testLookupLinInterValue}, - {"sensors", "testLookupLinInterInter", testLookupLinInterInter}, - {"sensors", "testLookupLinInterBelow", testLookupLinInterBelow}, - {"sensors", "testLookupLinInterAbove", testLookupLinInterAbove}, - {"sensors", "testToInfoLean", testToInfoLean}, - {"sensors", "testToInfoOkay", testToInfoOkay}, - {"sensors", "testToInfoIdeal", testToInfoIdeal}, - {"sensors", "testToInfoRich", testToInfoRich}, - {"strings", "testSplit", testSplit}, - {"strings", "testSplitSizeTooSmall", testSplitSizeTooSmall} +const char t01_P[] PROGMEM = "testSetupADC"; +const char t02_P[] PROGMEM = "testGetVoltage"; +const char t03_P[] PROGMEM = "testIsSimulation"; +const char t04_P[] PROGMEM = "testIsLogging"; +const char t05_P[] PROGMEM = "testCycle"; +const char t06_P[] PROGMEM = "testDivRoundNearest"; +const char t07_P[] PROGMEM = "testDivRoundNearestNumNeg"; +const char t08_P[] PROGMEM = "testDivRoundNearestDenNeg"; +const char t09_P[] PROGMEM = "testDivRoundNearestBothNeg"; +const char t10_P[] PROGMEM = "testDivRoundUp"; +const char t11_P[] PROGMEM = "testDivRoundUpNumNeg"; +const char t12_P[] PROGMEM = "testDivRoundUpDenNeg"; +const char t13_P[] PROGMEM = "testDivRoundUpBothNeg"; +const char t14_P[] PROGMEM = "testSetupPorts"; +const char t15_P[] PROGMEM = "testSetupSleepMode"; +const char t16_P[] PROGMEM = "testInitInterrupts"; +const char t17_P[] PROGMEM = "testInitInterrupts"; +const char t18_P[] PROGMEM = "testMeasure"; +const char t19_P[] PROGMEM = "testReadMeas"; +const char t20_P[] PROGMEM = "testToLambdaValue"; +const char t21_P[] PROGMEM = "testToLambdaInter"; +const char t22_P[] PROGMEM = "testToTempI"; +const char t23_P[] PROGMEM = "testToTempOValue"; +const char t24_P[] PROGMEM = "testToTempOInter"; +const char t25_P[] PROGMEM = "testLookupLinInterValue"; +const char t26_P[] PROGMEM = "testLookupLinInterInter"; +const char t27_P[] PROGMEM = "testLookupLinInterBelow"; +const char t28_P[] PROGMEM = "testLookupLinInterAbove"; +const char t29_P[] PROGMEM = "testToInfoLean"; +const char t30_P[] PROGMEM = "testToInfoOkay"; +const char t31_P[] PROGMEM = "testToInfoIdeal"; +const char t32_P[] PROGMEM = "testToInfoRich"; +const char t33_P[] PROGMEM = "testSplit"; +const char t34_P[] PROGMEM = "testSplitSizeTooSmall"; + +test const tests[] = { // PROGMEM? + {adc_P, t01_P, testSetupADC}, + {adc_P, t02_P, testGetVoltage}, + {command_P, t03_P, testIsSimulation}, + {command_P, t04_P, testIsLogging}, + {display_P, t05_P, testCycle}, + {integers_P, t06_P, testDivRoundNearest}, + {integers_P, t07_P, testDivRoundNearestNumNeg}, + {integers_P, t08_P, testDivRoundNearestDenNeg}, + {integers_P, t09_P, testDivRoundNearestBothNeg}, + {integers_P, t10_P, testDivRoundUp}, + {integers_P, t11_P, testDivRoundUpNumNeg}, + {integers_P, t12_P, testDivRoundUpDenNeg}, + {integers_P, t13_P, testDivRoundUpBothNeg}, + {interrupts_P, t14_P, testSetupPorts}, + {interrupts_P, t15_P, testSetupSleepMode}, + {interrupts_P, t16_P, testInitInterrupts}, + {interrupts_P, t17_P, testInitInterrupts}, + {sensors_P, t18_P, testMeasure}, + {sensors_P, t19_P, testReadMeas}, + {sensors_P, t20_P, testToLambdaValue}, + {sensors_P, t21_P, testToLambdaInter}, + {sensors_P, t22_P, testToTempI}, + {sensors_P, t23_P, testToTempOValue}, + {sensors_P, t24_P, testToTempOInter}, + {sensors_P, t25_P, testLookupLinInterValue}, + {sensors_P, t26_P, testLookupLinInterInter}, + {sensors_P, t27_P, testLookupLinInterBelow}, + {sensors_P, t28_P, testLookupLinInterAbove}, + {sensors_P, t29_P, testToInfoLean}, + {sensors_P, t30_P, testToInfoOkay}, + {sensors_P, t31_P, testToInfoIdeal}, + {sensors_P, t32_P, testToInfoRich}, + {strings_P, t33_P, testSplit}, + {strings_P, t34_P, testSplitSizeTooSmall} }; int main(void) { diff --git a/lambda/TODO b/lambda/TODO index e2dc7c5..c11df0e 100644 --- a/lambda/TODO +++ b/lambda/TODO @@ -1,6 +1,7 @@ * C -- What do static inline extern in C do? -- Read up on "global" variables and modules +- Use static global vars + getter or extern in tests? +- Use const for "final" function parameters +- Check other use of const (return value?) - Flexarray? http://en.wikipedia.org/wiki/Sizeof - License? (github) diff --git a/lambda/alert.c b/lambda/alert.c index 1a5740d..064cd97 100644 --- a/lambda/alert.c +++ b/lambda/alert.c @@ -12,10 +12,10 @@ #include "sensors.h" #include "display.h" -uint8_t oscCount = 0; -uint8_t beepCount = 0; -uint16_t beepLength = 0; -bool alertActive = false; +static uint8_t oscCount = 0; +static uint8_t beepCount = 0; +static uint16_t beepLength = 0; +static bool alertActive = false; void oscillateBeep(void) { if (beepCount == 0) { diff --git a/lambda/command.c b/lambda/command.c index 15f0389..ce0a4f6 100644 --- a/lambda/command.c +++ b/lambda/command.c @@ -16,8 +16,8 @@ #include "command.h" #include "strings.h" -bool simulation = false; -bool logging = false; +static bool simulation = false; +static bool logging = false; bool isSimulation(void) { return simulation; diff --git a/lambda/display.c b/lambda/display.c index e835fa9..b64e36a 100644 --- a/lambda/display.c +++ b/lambda/display.c @@ -20,11 +20,35 @@ #define MENU_OFF 0 #define MENU_MAX_VALUES 1 -uint8_t position = MENU_OFF; -bool updatePending = false; +static uint8_t position = MENU_OFF; +static bool updatePending = false; +static measurement measLatest; +static measurement measMax = {0, 0, 2000}; -measurement measLatest; -measurement measMax = {0, 0, 2000}; +// TODO unused/unnecessary "getter" does not add on program or data memory +// since only called from test, still a good idea? +uint8_t getPosition(void) { + return position; +} + +/** + * Formats the given measurement values and displays them on an 16x2 LCD along + * with the given hint. + */ +static void displayMeas(measurement meas, char* hint) { + uint16_t lambdax100 = divRoundNearest(meas.lambda, 10); + div_t lambdaT = div(lambdax100, 100); + + char line0[17]; + char line1[17]; + snprintf(line0, sizeof(line0), "Ti %3dC To %3dC ", meas.tempI, meas.tempO); + snprintf(line1, sizeof(line1), "L %d.%02d %s %s", + lambdaT.quot, abs(lambdaT.rem), toInfo(lambdax100), hint); + lcd_setcursor(0, 1); + lcd_string(line0); + lcd_setcursor(0, 2); + lcd_string(line1); +} void cycleDisplay(void) { updatePending = true; @@ -58,27 +82,19 @@ updatePending = true; } -void updateDisplay(void) { - if (isAlertActive()) { - return; - } +void updateDisplayIfPending() { + if (updatePending && ! isAlertActive()) { + updatePending = false; - updatePending = false; - - if (position == MENU_MAX_VALUES) { - displayMeas(measMax, "|>"); - } else { - displayMeas(measLatest, " "); + if (position == MENU_MAX_VALUES) { + displayMeas(measMax, "|>"); + } else { + displayMeas(measLatest, " "); + } } } -void updateDisplayIfRequested() { - if (updatePending) { - updateDisplay(); - } -} - -void printMeas(measurement meas) { +void logMeas(measurement meas) { char log[64]; snprintf(log, sizeof(log), "Ti %3d C - To %3d C - L %4u \r\n", @@ -86,21 +102,6 @@ printString(log); } -void displayMeas(measurement meas, char* hint) { - uint16_t lambdax100 = divRoundNearest(meas.lambda, 10); - div_t lambdaT = div(lambdax100, 100); - - char line0[17]; - char line1[17]; - snprintf(line0, sizeof(line0), "Ti %3dC To %3dC ", meas.tempI, meas.tempO); - snprintf(line1, sizeof(line1), "L %d.%02d %s %s", - lambdaT.quot, abs(lambdaT.rem), toInfo(lambdax100), hint); - lcd_setcursor(0, 1); - lcd_string(line0); - lcd_setcursor(0, 2); - lcd_string(line1); -} - void displayText(char* line0, char* line1) { lcd_clear(); lcd_setcursor(0, 1); diff --git a/lambda/display.h b/lambda/display.h index c769a19..18c2254 100644 --- a/lambda/display.h +++ b/lambda/display.h @@ -8,43 +8,33 @@ #ifndef DISPLAY_H_ #define DISPLAY_H_ +uint8_t getPosition(void); + /** - * Cycles through the "menu" (display options). Returns quickly so it can - * be called from an ISR. + * Cycles through the "menu" (display options) and flags pending display update. */ void cycleDisplay(void); /** * Updates the measurements, tracks max values since last start/reset - * and updates the display. + * and flags pending display update. */ void updateMeas(measurement); /** - * Resets max measurements to initial values. + * Resets max measurements to initial values and flags pending display update. */ void resetMeas(void); /** - * Updates the display with the selected measurement values. - */ -void updateDisplay(void); - -/** * Updates the display if an update is pending. */ -void updateDisplayIfRequested(void); +void updateDisplayIfPending(void); /** * Formats the given measurement values and prints them via USART. */ -void printMeas(measurement meas); - -/** - * Formats the given measurement values and displays them on an 16x2 LCD along - * with the given hint. - */ -void displayMeas(measurement meas, char* hint); +void logMeas(measurement meas); /** * Displays the given two lines of text. diff --git a/lambda/interrupts.c b/lambda/interrupts.c index 36db7b7..01fbbf0 100644 --- a/lambda/interrupts.c +++ b/lambda/interrupts.c @@ -14,11 +14,11 @@ #include "display.h" #include "alert.h" -volatile bool buttonPressed = false; -volatile bool usartReceived = false; -volatile uint8_t intCount = 0; +static volatile bool buttonPressed = false; +static volatile bool usartReceived = false; +static volatile uint8_t intCount = 0; -char usartData[64]; +static char usartData[64]; /** * Called every 16 ms. @@ -116,7 +116,7 @@ // timer1 clock prescaler/8 TCCR1B |= (1 << CS11); // toggles PB1 at 7.8 kHz generating a 3.9 kHz beep - // OCR1A = 16; + // OCR1A = 15; // 1.8 kHz is less noisy on the small piezo beeper - OCR1A = 32; + OCR1A = 31; } diff --git a/lambda/lambda.c b/lambda/lambda.c index 9ad7a58..b4d1ce4 100644 --- a/lambda/lambda.c +++ b/lambda/lambda.c @@ -53,7 +53,7 @@ if (hasIntCount(62, true) && ! isSimulation()) { meas = measure(); if (isLogging()) { - printMeas(meas); + logMeas(meas); } updateMeas(meas); } @@ -62,7 +62,7 @@ getUSARTData(data, sizeof(data)); runCommand(data); } - updateDisplayIfRequested(); + updateDisplayIfPending(); } // never reached diff --git a/lambda/sensors.c b/lambda/sensors.c index 5bc571b..3deed63 100644 --- a/lambda/sensors.c +++ b/lambda/sensors.c @@ -62,9 +62,9 @@ /** * Variables holding averaged voltages*8. */ -uint32_t lambdaVoltageAvg = 44 << 3; // Lambda 2.00 -uint32_t tempIVoltageAvg = 100 << 3; // 20°C -uint32_t tempOVoltageAvg = 644 << 3; // 20°C +static uint32_t lambdaVoltageAvg = 44 << 3; // Lambda 2.00 +static uint32_t tempIVoltageAvg = 100 << 3; // 20°C +static uint32_t tempOVoltageAvg = 644 << 3; // 20°C /** * Measures the "input" and "output" temperatures and the lambda value, @@ -93,6 +93,7 @@ measurement readMeas(char* fields[]) { measurement meas; + // TODO can check if fields[] has 3 elements? meas.tempI = atoi(fields[0]); meas.tempO = atoi(fields[1]); meas.lambda = atoi(fields[2]); @@ -142,7 +143,7 @@ return value; } -const char* toInfo(uint16_t lambda) { +char* toInfo(uint16_t lambda) { if (lambda > 190) { return LEAN; } else if (lambda > 150 && lambda <= 190) { diff --git a/lambda/sensors.h b/lambda/sensors.h index 7c93e8e..336b296 100644 --- a/lambda/sensors.h +++ b/lambda/sensors.h @@ -86,6 +86,6 @@ * For a wood fire, residual oxygen between 5% and 7% (lambda 1.3 and 1.5) is * a good value, below is rich and above is lean. */ -const char* toInfo(uint16_t lambda); +char* toInfo(uint16_t lambda); #endif /* SENSORS_H_ */