diff --git a/lambda-test/display-test.c b/lambda-test/display-test.c index 1d62d72..311cc3b 100644 --- a/lambda-test/display-test.c +++ b/lambda-test/display-test.c @@ -27,7 +27,7 @@ static bool testCycle(void) { cancelAlert(true); - position = displayPosCurrent;; + position = displayPosCurrent; updatePending = false; assertTrue(position == displayPosCurrent); @@ -37,7 +37,7 @@ assertTrue(position == displayPosMax); assertTrue(updatePending); assertTrue(beepCount == 1); - assertTrue(beepLength == 2); + assertTrue(beepLength == 1); cycleDisplay(); assertTrue(position == displayPosHeater); @@ -67,8 +67,6 @@ } static bool testUpdateMeas(void) { - updatePending = false; - // initial measurements assertTrue(measLatest.tempI == 0); assertTrue(measLatest.tempO == 0); @@ -90,7 +88,6 @@ assertTrue(measMax.tempI == 1); assertTrue(measMax.tempO == 2); assertTrue(measMax.lambda == 3); - assertTrue(updatePending); Measurement meas2 = {10, 20, 30, 40}; updateMeas(meas2); diff --git a/lambda-test/interrupts-test.c b/lambda-test/interrupts-test.c index 76c54c3..1ed25af 100644 --- a/lambda-test/interrupts-test.c +++ b/lambda-test/interrupts-test.c @@ -46,11 +46,10 @@ // ADC interrupt enabled assertTrue(bit_is_set(ADCSRA, ADIE)); - // PC interrupts enabled - // assertTrue(bit_is_set(PCICR, PCIE0)); - // assertTrue(bit_is_set(PCMSK0, PB0)); - // enable timer 0 overflow interrupt - assertTrue(bit_is_set(TIMSK0, TOIE0)); + + // timer0 compare match A interrupt enabled + assertTrue(bit_is_set(TIMSK0, OCIE0A)); + // USART RX complete interrupt 0 enabled assertTrue(bit_is_set(UCSR0B, RXCIE0)); @@ -63,17 +62,20 @@ static bool testInitTimers(void) { initTimers(); - // timer0 clock prescaler /64 = 15.625 kHz overflowing every 16.2 ms - uint8_t prescalerBy64 = (1 << CS00) | (1 << CS01); - assertTrue((TCCR0B & prescalerBy64) == prescalerBy64); + // timer0 clear timer on compare match mode, TOP OCR0A + assertTrue(bit_is_set(TCCR0A, WGM01)); + // timer0 clock prescaler/256 + uint8_t timer0Prescaler = (1 << CS02); + assertTrue((TCCR0B & timer0Prescaler) == timer0Prescaler); + // timer0 compare match + assertTrue(OCR0A == 122); - // timer1 Clear Timer on Compare Match mode, TOP OCR1A + // timer1 clear timer on compare match mode, TOP OCR1A assertTrue(bit_is_set(TCCR1B, WGM12)); // timer1 clock prescaler/8 - assertTrue(bit_is_set(TCCR1B, CS11)); - // toggles PB1 at 7.8 kHz generating a 3.9 kHz beep - // assertTrue(OCR1A == 15); - // 2 kHz is less noisy on the small piezo beeper + uint8_t timer1Prescaler = (1 << CS11); + assertTrue((TCCR1B & timer1Prescaler) == timer1Prescaler); + // timer1 compare match assertTrue(OCR1A == 31); return true; @@ -81,16 +83,14 @@ static bool testTime(void) { resetTime(); - assertTrue(getInts() == 0); assertTrue(getTime() == 0); _delay_ms(1000); - assertTrue(getInts() >= INTS_PER_SEC); assertTrue(getTime() == 1); // add 1:11:10 - addInts(4270UL * INTS_PER_SEC); + addTime(4270UL); // HHHHH:MM:SS char str[12]; diff --git a/lambda-test/release-test.c b/lambda-test/release-test.c index b93e8b4..d29edd3 100644 --- a/lambda-test/release-test.c +++ b/lambda-test/release-test.c @@ -31,9 +31,6 @@ assertTrue(PD3 == LCD_DB6); assertTrue(PD2 == LCD_DB7); - // average value - assertTrue(61 == INTS_PER_SEC); - // nominal value assertTrue(100 == SHUNT_MILLIOHMS); diff --git a/lambda-test/rules-test.c b/lambda-test/rules-test.c index 147660d..fba85dc 100644 --- a/lambda-test/rules-test.c +++ b/lambda-test/rules-test.c @@ -349,7 +349,7 @@ setHeaterOn(true); // more than 3 mins - addInts(INTS_PER_SEC * 181); + addTime(181); meas.current = 5000; reason(meas); @@ -370,7 +370,7 @@ setHeaterOn(true); // more than 3 hours below 400°C - addInts(INTS_PER_SEC * 10800UL); + addTime(10800UL); meas.tempI = 300; meas.current = 1300; diff --git a/lambda/adc.c b/lambda/adc.c index 526732a..de4841f 100644 --- a/lambda/adc.c +++ b/lambda/adc.c @@ -24,8 +24,7 @@ // disable digital input on the ADC inputs // http://www.openmusiclabs.com/learning/digital/atmega-adc/ DIDR0 = 0b00111111; - // ADC clock prescaler/8 - ADCSRA |= (1 << ADPS1) | (1 << ADPS0); + ADCSRA |= ADC_PRESCALE; // enable ADC ADCSRA |= (1 << ADEN); } diff --git a/lambda/adc.h b/lambda/adc.h index 15fb225..3e8aac3 100644 --- a/lambda/adc.h +++ b/lambda/adc.h @@ -16,6 +16,14 @@ #define ADC_NONLIN_0 -8 #define ADC_NONLIN_AREF 12 +#if F_CPU == 1000000 + // ADC clock prescaler/8 = 125kHz @ 1MHz + #define ADC_PRESCALE (1 << ADPS1) | (1 << ADPS0) +#elif F_CPU == 8000000 + // ADC clock prescaler/64 = 125kHz @ 8MHz + #define ADC_PRESCALE (1 << ADPS2) | (1 << ADPS1) +#endif + /** * Sets up reference voltage and clock prescaler of the ADC and enables it. */ diff --git a/lambda/command.c b/lambda/command.c index bc2eab3..7c2f589 100644 --- a/lambda/command.c +++ b/lambda/command.c @@ -44,7 +44,7 @@ resetDisplay(); resetRules(); setHeaterOn(true); - beep(1, 2, 31); + beep(1, 1, 31); } else if (strcmp_P(fields[0], PSTR("sd")) == 0) { // simulation disable @@ -52,27 +52,27 @@ resetDisplay(); resetRules(); simulation = false; - beep(1, 2, 31); + beep(1, 1, 31); } else if (strcmp_P(fields[0], PSTR("le")) == 0) { // logging enable logging = true; - beep(1, 2, 31); + beep(1, 1, 31); } else if (strcmp_P(fields[0], PSTR("ld")) == 0) { // logging disable logging = false; - beep(1, 2, 31); + beep(1, 1, 31); } else if (strcmp_P(fields[0], PSTR("he")) == 0) { // oxygen sensor heater enable setHeaterOn(true); - beep(1, 2, 31); + beep(1, 1, 31); } else if (strcmp_P(fields[0], PSTR("hd")) == 0) { // oxygen sensor heater disable setHeaterOn(false); - beep(1, 2, 31); + beep(1, 1, 31); } else if (strcmp_P(fields[0], PSTR("cm")) == 0) { // cycle menu @@ -82,7 +82,7 @@ // test alert char buf[16]; strcpy_P(buf, PSTR("Beep Beep Beep!")); - alert(3, 10, 15, buf, fields[1], false); + alert(3, 5, 15, buf, fields[1], false); } else if (strcmp_P(fields[0], PSTR("tb")) == 0) { // test beep @@ -97,15 +97,16 @@ beep(1, length, tone); } else if (simulation) { - // add one second per measurement to the timebase, + // add one second per measurement to the time, // assuming one measurement was logged per second - addInts(INTS_PER_SEC); + addTime(1); Measurement meas = readMeas(fields, fieldCount); if (getHeaterState() == heaterStateOff || getHeaterState() == heaterStateReady) { updateMeas(meas); } reason(meas); + setUpdatePending(); } updateDisplayIfPending(); diff --git a/lambda/display.c b/lambda/display.c index b932ad2..a3c12bc 100644 --- a/lambda/display.c +++ b/lambda/display.c @@ -99,7 +99,7 @@ position = displayPosCurrent; } updatePending = true; - beep(1, 2, 31); + beep(1, 1, 31); } void updateMeas(Measurement const meas) { @@ -108,8 +108,6 @@ measMax.tempI = MAX(measMax.tempI, meas.tempI); measMax.tempO = MAX(measMax.tempO, meas.tempO); measMax.lambda = MIN(measMax.lambda, meas.lambda); - - updatePending = true; } void resetDisplay(void) { @@ -159,3 +157,7 @@ strncat(lastLine1, line1, sizeof(lastLine1) - 1); setText(line0, line1); } + +void setUpdatePending(void) { + updatePending = true; +} diff --git a/lambda/display.h b/lambda/display.h index efaf506..4a51fb0 100644 --- a/lambda/display.h +++ b/lambda/display.h @@ -57,4 +57,10 @@ */ void displayText(const char* line0, const char* line1); +/** + * Causes the display to be updated next time + * updateDisplayIfPending() is called. + */ +void setUpdatePending(void); + #endif /* DISPLAY_H_ */ diff --git a/lambda/interrupts.c b/lambda/interrupts.c index 6779e27..95ce891 100644 --- a/lambda/interrupts.c +++ b/lambda/interrupts.c @@ -24,37 +24,35 @@ static volatile uint32_t ints = 0; /** - * Called about every 16.4 ms. + * Called about every 1/32 seconds. */ -ISR(TIMER0_OVF_vect) { +ISR(TIMER0_COMPA_vect) { if (! isSimulation()) { ints++; } oscillateBeep(); - if (bit_is_clear(PINB, PB0) && ! buttonPressed) { + if (bit_is_clear(PIN, PIN_BUTTON) && ! buttonPressed) { buttonPressed = true; cycleDisplay(); - } else if (bit_is_set(PINB, PB0)) { + } else if (bit_is_set(PIN, PIN_BUTTON)) { buttonPressed = false; } } -uint32_t getInts(void) { - uint32_t atomicInts; - ATOMIC_BLOCK(ATOMIC_FORCEON) { - atomicInts = ints; - } - return atomicInts; -} - -void addInts(uint32_t const add) { - ATOMIC_BLOCK(ATOMIC_FORCEON) { - ints += add; - } -} - uint32_t getTime(void) { - return getInts() / INTS_PER_SEC; + uint32_t time; + ATOMIC_BLOCK(ATOMIC_FORCEON) { + // 32 ints per second + time = ints >> 5; + } + + return time; +} + +void addTime(uint32_t const time) { + ATOMIC_BLOCK(ATOMIC_FORCEON) { + ints += (time << 5); + } } void formatTime(char* const str, size_t const size) { @@ -92,29 +90,24 @@ // enable ADC interrupt ADCSRA |= (1 << ADIE); - // enable timer 0 overflow interrupt - TIMSK0 |= (1 << TOIE0); + // enable timer0 compare match A interrupt + TIMSK0 |= (1 << OCIE0A); // enable USART RX complete interrupt 0 UCSR0B |= (1 << RXCIE0); - // enable data register empty interrupt 0 - // UCSR0B |= (1 << UDRIE0); // enable global interrupts sei(); } void initTimers(void) { - // timer in normal mode is default - // timer0 clock prescaler/64 = 15.625 kHz overflowing every 16.4 ms - TCCR0B |= (1 << CS01) | (1 << CS00); + // timer0 clear timer on compare match mode, TOP OCR0A + TCCR0A |= (1 << WGM01); + TCCR0B |= TIMER0_PRESCALE; + OCR0A = TIMER0_COMP_MATCH; - // timer1 Clear Timer on Compare Match mode, TOP OCR1A + // timer1 clear timer on compare match mode, TOP OCR1A TCCR1B |= (1 << WGM12); - // timer1 clock prescaler/8 - TCCR1B |= (1 << CS11); - // timer1 Compare Match at 7.8 kHz generating a 3.9 kHz beep - // OCR1A = 15; - // 2 kHz is less noisy on the small piezo beeper - OCR1A = 31; + TCCR1B |= TIMER1_PRESCALE; + OCR1A = TIMER1_COMP_MATCH; } diff --git a/lambda/interrupts.h b/lambda/interrupts.h index c148fbb..436faa1 100644 --- a/lambda/interrupts.h +++ b/lambda/interrupts.h @@ -11,20 +11,29 @@ #ifndef INTERRUPTS_H_ #define INTERRUPTS_H_ -// 61 +/-1 depending on internal oscillator? -#define INTS_PER_SEC 61 - -/** - * Returns the count of interrupts occurring about every 16.4 ms - * since last reset. - */ -uint32_t getInts(void); - -/** - * Adds the given number of interrupts to the interrupt counter - * used as timebase. Useful only for simulation. - */ -void addInts(uint32_t add); +#if F_CPU == 1000000 + // timer0 clock prescaler/256 = 3.906 kHz @ 1 MHz + #define TIMER0_PRESCALE (1 << CS02) + // timer0 compare match about every 31.25 ms = 1000 ms >> 5 + #define TIMER0_COMP_MATCH 122 + // timer1 clock prescaler/8 = 125 kHz @ 1MHz + #define TIMER1_PRESCALE (1 << CS11) + // timer1 compare match at 7.8 kHz generating a 3.9 kHz beep + // #define TIMER1_CTC_TOP 15 + // 2 kHz is less noisy on the small piezo beeper + #define TIMER1_COMP_MATCH 31 +#elif F_CPU == 8000000 + // timer0 clock prescaler/1024 = 7.812 kHz @ 8 MHz + #define TIMER0_PRESCALE (1 << CS02) | (1 << CS00) + // timer0 compare match about every 31.25 ms = 1000 ms >> 5 + #define TIMER0_COMP_MATCH 244 + // timer1 clock prescaler/64 = 125 kHz @ 8MHz + #define TIMER1_PRESCALE (1 << CS11) | (1 << CS10) + // timer1 compare match at 7.8 kHz generating a 3.9 kHz beep + // #define TIMER1_CTC_TOP 15 + // 2 kHz is less noisy on the small piezo beeper + #define TIMER1_COMP_MATCH 31 +#endif /** * Returns the time in about seconds since last reset. @@ -32,6 +41,11 @@ uint32_t getTime(void); /** + * Adds the given number of seconds. Useful only for simulation. + */ +void addTime(uint32_t time); + +/** * Writes the time since last reset in format HHHHH:MM:SS to the given string. */ void formatTime(char* str, size_t size); diff --git a/lambda/lambda.c b/lambda/lambda.c index 48eb8e6..873537c 100644 --- a/lambda/lambda.c +++ b/lambda/lambda.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "usart.h" #include "lcdroutines.h" #include "adc.h" @@ -39,6 +40,12 @@ * runs commands sent via USART. */ int main(void) { + + // assuming 1 MHz default + if (F_CPU == 8000000UL) { + clock_prescale_set(clock_div_1); + } + initUSART(); lcd_init(); setupPorts(); @@ -47,24 +54,25 @@ initInterrupts(); initTimers(); - alert_P(1, 2, 31, PSTR(MSG_WELCOME), PSTR(""), false); + alert_P(1, 1, 31, PSTR(MSG_WELCOME), PSTR(""), false); // spend some time on being polite while (getTime() < 3) {} setHeaterOn(true); - uint32_t ints = 0; + uint32_t time = 0; Measurement meas; // main loop while (true) { - if (! isSimulation() && getInts() >= ints + INTS_PER_SEC) { - ints = getInts(); + if (! isSimulation() && getTime() > time) { + time = getTime(); meas = measure(); updateMeas(meas); reason(meas); if (isLogging()) { logMeas(meas); } + setUpdatePending(); } if (! isSimulation()) { updateDisplayIfPending(); diff --git a/lambda/pins.h b/lambda/pins.h index f307075..244c81d 100644 --- a/lambda/pins.h +++ b/lambda/pins.h @@ -21,7 +21,9 @@ /** ADC pin for the LSM 11 oxygen sensor heater current */ #define ADC_HEATER PC3 -/** Port for menu button, beeper and oxygen sensor heater */ +/** Pin input register for menu button */ +#define PIN PINB +/** Pin output register for menu button, beeper and oxygen sensor heater */ #define PORT PORTB /** DDR for menu button, beeper and oxygen sensor heater */ #define DDR DDRB diff --git a/lambda/rules.c b/lambda/rules.c index 2fa7f47..c083b08 100644 --- a/lambda/rules.c +++ b/lambda/rules.c @@ -13,7 +13,7 @@ #include "messages.h" #define BEEPS 30 -#define LENGTH 20 +#define LENGTH 10 #define TONE 31 uint8_t age = 0; @@ -126,7 +126,7 @@ } if (meas.current <= milliAmpsReady && meas.current > milliAmpsDisconn) { setHeaterState(heaterStateReady); - alert_P(3, 10, TONE, PSTR(MSG_HEATER_READY_0), + alert_P(3, 5, TONE, PSTR(MSG_HEATER_READY_0), PSTR(MSG_HEATER_READY_1), false); } } @@ -158,7 +158,7 @@ Measurement const meas) { if (isHeaterOn() && getTime() >= 10800 && meas.tempI < 400) { setHeaterOn(false); - alert_P(3, 10, TONE, PSTR(MSG_HEATER_OFF_0), + alert_P(3, 5, TONE, PSTR(MSG_HEATER_OFF_0), PSTR(MSG_HEATER_OFF_1), false); } } diff --git a/lambda/sensors.c b/lambda/sensors.c index dee5abd..d87240c 100644 --- a/lambda/sensors.c +++ b/lambda/sensors.c @@ -195,7 +195,7 @@ if (on) { PORTB |= (1 << PB2); heaterState = heaterStateUp; - alert_P(1, 2, 31, PSTR(MSG_HEATER_UP_0), PSTR(MSG_HEATER_UP_1), true); + alert_P(1, 1, 31, PSTR(MSG_HEATER_UP_0), PSTR(MSG_HEATER_UP_1), true); } else { PORTB &= ~(1 << PB2); heaterState = heaterStateOff;