diff --git a/lambda/integers.c b/lambda/integers.c index 8d874a5..00984a7 100644 --- a/lambda/integers.c +++ b/lambda/integers.c @@ -13,3 +13,9 @@ ((num + den / 2) / den); } +int32_t roundUp(int32_t num, int32_t den) { + return ((num < 0) ^ (den < 0)) ? + ((num - den + 1) / den) : + ((num + den - 1) / den); +} + diff --git a/lambda/integers.h b/lambda/integers.h index 0058661..c8f678b 100644 --- a/lambda/integers.h +++ b/lambda/integers.h @@ -6,10 +6,16 @@ */ /** - * Divides the given numerator by the given denominator - * to the nearest int and returns it. + * Divides the given numerator by the given denominator, + * rounds to the nearest int and returns it. * http://stackoverflow.com/a/18067292/709426 */ int32_t roundNearest(int32_t num, int32_t den); +/** + * Divides the given numerator by the given denominator, + * rounds up and returns it. + */ +int32_t roundUp(int32_t num, int32_t den); + // char* toDecimalString(int32_t num, int32_t den); diff --git a/lambda/lambda.c b/lambda/lambda.c index 9d9234e..b388128 100644 --- a/lambda/lambda.c +++ b/lambda/lambda.c @@ -4,9 +4,8 @@ * Created on: 22.02.2015 * Author: dode@luniks.net * - * TODO comments, attribution - * TODO DIDR? - * TODO string.h? + * TODO attribution + * TODO have a look at string.h */ #include #include @@ -19,12 +18,18 @@ #include "sensors.h" #include "integers.h" +/** + * Global variables holding averaged voltages. + */ int16_t lambdaVoltageAvg = 0; int16_t tempIVoltageAvg = 0; int16_t tempOVoltageAvg = 0; EMPTY_INTERRUPT(ADC_vect); +/** + * Sets up reference voltage and clock prescaler of the ADC and enables it. + */ void setupADC(void) { ADMUX |= (1 << REFS0); // use AVCC as reference voltage // ADCSRA |= (1 << ADPS1) | (1 << ADPS2); // ADC clock prescaler /64 @@ -32,12 +37,19 @@ ADCSRA |= (1 << ADEN); // enable ADC } +/** + * Sets up sleep mode and enables ADC and global interrupts. + */ void setupSleepMode(void) { set_sleep_mode(SLEEP_MODE_ADC); ADCSRA |= (1 << ADIE); // enable ADC interrupt sei(); // enable global interrupts } +/** + * Formats the given values, displays them on an 16x2 LCD + * and prints them over USART. + */ void display( int16_t tempIVoltage, int16_t tempI, int16_t tempOVoltage, int16_t tempO, @@ -53,10 +65,18 @@ printString(line1); } -int16_t average(int16_t voltage, int16_t average, uint8_t weight) { - return roundNearest(voltage + (average * weight) + weight, weight + 1); +/** + * Creates an exponential moving average of the given value and + * average weighted by the given weight. + */ +int16_t average(int16_t value, int16_t average, uint8_t weight) { + return roundUp(value + (average * weight), weight + 1); } +/** + * Measures the "input" and "output" temperatures and the lambda value + * and displays the measured values. + */ void measure(void) { int16_t tempIVoltage = getVoltage(PC5); tempIVoltageAvg = average(tempIVoltage, tempIVoltageAvg, 4); @@ -75,6 +95,13 @@ display(tempIVoltageAvg, tempI, tempOVoltageAvg, tempO, lambdaVoltageAvg, lambda); } +/** + * Initializes the USART transmitter and receiver, sets up the ADC + * and sleep mode and then infinitely measures with a 1 second delay + * in between. + * TODO DIDR? + * TODO replace delay by an interrupt or something else more efficient? + */ int main(void) { initUSART(); @@ -91,5 +118,6 @@ _delay_ms(1000); } + // will never be reached return 0; } diff --git a/lambda/sensors.c b/lambda/sensors.c index 457df80..6476996 100644 --- a/lambda/sensors.c +++ b/lambda/sensors.c @@ -13,21 +13,11 @@ #include "sensors.h" #include "integers.h" -// #define AREF_MV 4850 -#define AREF_MV 5000 -#define ADC_OFFSET_MV 7 -// #define TMP_OP_OFFSET_MV 441 -#define TMP_OP_OFFSET_MV 454 - -static const char* lean = "Mager"; -static const char* ideal = "Ideal"; -static const char* rich = "Fett!"; - /** * Table used to look up the lambda value at 12 V heater voltage * and 220°C exhaust gas temperature. Most values are approximated * from the characteristic curve in the datasheet. - * TODO equation? real table? + * TODO real data? */ static const tableEntry lambdaTable[] = { { 4, 2000 }, @@ -47,6 +37,12 @@ { 880, 800 } }; +/** + * Table used to look up the temperature in °C at a given voltage + * measured using a wheatstone bridge and amplified with a non- + * inverting OP with an offset of 454 mV at 5000 mV supply voltage + * and an amplification factor of 6.17. + */ static const tableEntry tempOTable[] = { { -57, -50 }, { 454, 0 }, @@ -70,13 +66,6 @@ return mV; } -int16_t toLambda(int16_t mV) { - uint8_t length = sizeof(lambdaTable) / sizeof(lambdaTable[0]); - int16_t lambda = lookupLinInter(mV, lambdaTable, length); - - return lambda; -} - int16_t toTempI(int16_t mV) { int temp = roundNearest(mV, 5); @@ -90,6 +79,13 @@ return temp; } +int16_t toLambda(int16_t mV) { + uint8_t length = sizeof(lambdaTable) / sizeof(lambdaTable[0]); + int16_t lambda = lookupLinInter(mV, lambdaTable, length); + + return lambda; +} + int16_t lookupLinInter(int16_t mV, const tableEntry table[], uint8_t length) { if (mV < table[0].mV) { return table[0].value; @@ -114,11 +110,11 @@ const char* toInfo(int16_t lambda) { if (lambda > 1500) { - return lean; + return LEAN; } else if (lambda > 1300 && lambda <= 1500) { - return ideal; + return IDEAL; } else { - return rich; + return RICH; } } diff --git a/lambda/sensors.h b/lambda/sensors.h index 6a84d25..ff9f704 100644 --- a/lambda/sensors.h +++ b/lambda/sensors.h @@ -4,15 +4,33 @@ * Created on: 02.03.2015 * Author: dode@luniks.net */ + +// TODO is this the right place for these definitions? Put in makefile? +// #define AREF_MV 4850 +#define AREF_MV 5000 +#define ADC_OFFSET_MV 7 +// #define TMP_OP_OFFSET_MV 441 +#define TMP_OP_OFFSET_MV 454 + +#define LEAN "Mager" +#define IDEAL "Ideal"; +#define RICH "Fett!"; + +/** + * Struct used as entries for the lookup tables. + */ typedef struct { const int16_t mV; const int16_t value; } tableEntry; +/** + * Returns the voltage sampled at the given ADC input port doing + * 16x oversampling and taking in account the calibrated AREF and + * ADC offset voltages. + */ int16_t getVoltage(uint8_t port); -int16_t toLambda(int16_t mV); - /** * Returns the temperature for the given voltage of a type K thermocouple * amplified with an AD8495 (5 mV/°C). Type K thermocouple voltages are @@ -20,14 +38,27 @@ */ int16_t toTempI(int16_t mV); +/** + * Looks up the temperature in °C mapped to the given voltage measured at + * the PT1000 using linear interpolation and returns it. + */ int16_t toTempO(int16_t mV); /** + * Looks up the lambda value x1000 mapped to the given voltage measured at + * the oxygen sensor using linear interpolation and returns it. + */ +int16_t toLambda(int16_t mV); + +/** * Returns the value corresponding to the given voltage - * from the lookup table using linear interpolation. + * from the given lookup table using linear interpolation. * Thanks to http://stackoverflow.com/a/7091629/709426 and * http://en.wikipedia.org/wiki/Linear_interpolation */ int16_t lookupLinInter(int16_t mV, const tableEntry table[], uint8_t length); +/** + * Returns a descriptive term such as "Lean" for the given lambda value x1000. + */ const char* toInfo(int16_t lambda);