Plate warmer firmware.
authorPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 21 Mar 2013 11:34:42 +0000 (11:34 +0000)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 21 Mar 2013 11:34:42 +0000 (11:34 +0000)
PlateWarmer.ino [new file with mode: 0644]

diff --git a/PlateWarmer.ino b/PlateWarmer.ino
new file mode 100644 (file)
index 0000000..22a45ab
--- /dev/null
@@ -0,0 +1,361 @@
+/*                                                           -*- c++ -*-
+ * We have a LM35 temperature sensor connected to pin A0 and we
+ * shall use the internal 1.1V AREF for the ADC so we have a 10nF
+ * capacitor connecting AREF to GND.
+ *
+ * We then have a LTC-4727C 4 digit seven segment display wired up to
+ * Arduino pins 2-13 as in previous demos (eg TempDisplay).
+ *
+ * Otherwise the setup is a basic breadboard arduino (ATmega328)
+ *
+ */
+
+#include <Arduino.h>
+#include <EEPROM.h>
+#include <Ports.h> // JeeLib library
+#include <avr/pgmspace.h>
+#include <avr/eeprom.h>
+
+#define COMMON_CATHODE // This device is common cathode
+#define HAVE_SHIFT_REGISTER 1 // We are using a shift register
+
+// declare the digital pins for each segment led
+#define SEG_A 6
+#define SEG_B 8
+#define SEG_C 5
+#define SEG_D 2
+#define SEG_E 3
+#define SEG_F 4
+#define SEG_G 7
+#define SEG_DP 13
+
+#define LED_PIN 13
+#define SHIFTREG_DATA  4 // DS pin
+#define SHIFTREG_CLOCK 3 // SHCP pin
+#define SHIFTREG_LATCH 2 // STCP pin
+
+#define BUTTON_L 6 // left button
+#define BUTTON_R 5 // right button
+
+// declare the digit selector pins
+#define SEL_1 12
+#define SEL_2 11
+#define SEL_3 10
+#define SEL_4 9
+const int select[4] = { SEL_1, SEL_2, SEL_3, SEL_4 };
+
+#define LM35_PIN A0
+#define NUMSAMPLES 5
+
+#ifdef COMMON_CATHODE
+#define SEG_OFF LOW
+#define SEG_ON  HIGH
+#else
+#define SEG_OFF HIGH
+#define SEG_ON  LOW
+#endif
+
+// Table of the bit patterns for each digit in gfedcba order
+const byte Digits[10] = {
+    0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f
+};
+
+// Buffer to hold the current display characters.
+// This is read by the timer interrupt
+static char Display[4] = {0, 0, 0, 0};
+
+// The timer interrupt sets one digit at a time and that digit is
+// shown until the next interrupt. This happens quickly enough that we
+// don't see any flicker and all the digits appear to be lit all the
+// time. This variable selects the digit to show and rotates around
+// our buffer.
+volatile int current_digit = 0;
+
+// The timer initial counter value.
+static unsigned int tcnt2 = 0;
+
+static unsigned int led_cnt = 0;
+
+// Flag to make the display flash visibly
+#define STATE_NORMAL 0
+#define STATE_USERPRESS 1
+#define STATE_FLASHING 2
+static byte display_state = STATE_NORMAL;
+static byte flash_cntr = 0;
+static long target_temp = 0;
+static long last_target = 0;
+
+// Serial print using FLASH strings: Serial_print(PSTR(...));
+static void Serial_print(PGM_P s)
+{
+    char c;
+    while ((c = pgm_read_byte(s++)) != 0)
+        Serial.print(c);
+}
+
+// Set the I/O lines for the digit bitpattern for the currently
+// selected digit.
+static void
+set_segments(byte v)
+{
+#if HAVE_SHIFT_REGISTER
+    digitalWrite(SHIFTREG_LATCH, LOW);
+    shiftOut(SHIFTREG_DATA, SHIFTREG_CLOCK, MSBFIRST, v);
+    digitalWrite(SHIFTREG_LATCH, HIGH);
+#else
+    digitalWrite(SEG_G, (v & 0x40) ? SEG_ON : SEG_OFF);
+    digitalWrite(SEG_F, (v & 0x20) ? SEG_ON : SEG_OFF);
+    digitalWrite(SEG_E, (v & 0x10) ? SEG_ON : SEG_OFF);
+    digitalWrite(SEG_D, (v & 0x08) ? SEG_ON : SEG_OFF);
+    digitalWrite(SEG_C, (v & 0x04) ? SEG_ON : SEG_OFF);
+    digitalWrite(SEG_B, (v & 0x02) ? SEG_ON : SEG_OFF);
+    digitalWrite(SEG_A, (v & 0x01) ? SEG_ON : SEG_OFF);
+#endif
+}
+
+// Select one digit and display the value using the lookup table to
+// get the bit pattern. This will be on until the next timer
+// interrupt.
+static void
+display(int digit, const char *num)
+{
+    // clear all select lines
+    digitalWrite(SEL_1, HIGH);
+    digitalWrite(SEL_2, HIGH);
+    digitalWrite(SEL_3, HIGH);
+    digitalWrite(SEL_4, HIGH);
+
+    // If flashing, leave off half the time.
+    if (display_state == STATE_FLASHING) {
+        ++flash_cntr;
+        if (flash_cntr > 127)
+            return;
+    }
+
+    // select our digit and configure
+    digitalWrite(select[digit], LOW);
+#if HAVE_SHIFT_REGISTER
+    set_segments( Digits[num[digit] - 0x30] | ((digit == 1) ? 0x80 : 0) );
+#else
+    set_segments(Digits[num[digit] - 0x30]);
+    // set the decimal point on digit 2
+    digitalWrite(SEG_DP, (digit == 1) ? SEG_ON : SEG_OFF);
+#endif
+}
+
+// Initialize timer2 as interrupt source
+// See section 18 of the datasheet
+// Updating the display at 6ms intervals is as slow as we can get
+// before we start to notice flickering.
+void
+Timer2Init()
+{
+    // 18.9: Disable the timer2 overflow interrupt for configuration
+    TIMSK2 &= ~(1 << TOIE2);
+    // 18.9b: Select clock source as internal i/o clock
+    ASSR &= ~(1<<AS2);
+    // Table 18.8: all 0 == normal operation with overflow
+    TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
+    TCCR2B &= ~(1<<WGM22);
+    // Table 18-9: Configure prescaler to CPU/1024
+    TCCR2B |= (1<<CS22) | (1<<CS21) | (1<<CS20);
+    // Delay is (prescale / cpu_freq * 1e6)
+    // [@16MHz with 1024 prescaler yields 75us tick]
+    // So set tcnt2 = 256 - (6000us / 75us) for 6ms update
+    tcnt2 = 176;
+    TCNT2 = tcnt2;
+    // 18.9e: re-enable the timer overflow interrupt flag
+    TIMSK2 |= (1 << TOIE2);
+}
+
+ISR(TIMER2_OVF_vect)
+{
+    TCNT2 = tcnt2; // reset the timer
+    display(current_digit, Display);
+    current_digit = (current_digit + 1) % 4;
+    ++led_cnt;
+    if (led_cnt > 166) {
+        PORTB ^= _BV(PB5);
+        led_cnt = 0;
+    }
+}
+
+// We store the currently set target temperature in the EEPROM
+// as a 32 bit little-endian integer.
+static long stored_target_read()
+{
+    long t = 2000;
+    byte check = EEPROM.read(3);
+    if (check != 0xff) {
+        t = (check << 24)
+            | (EEPROM.read(2) << 16)
+            | (EEPROM.read(1) << 8)
+            | (EEPROM.read(0));
+    }
+    Serial_print(PSTR("eeprom read: "));
+    Serial.println(t);
+    return t;
+}
+
+static void stored_target_write(long t)
+{
+    EEPROM.write(0, t & 0xff);
+    EEPROM.write(1, (t >> 8) & 0xff);
+    EEPROM.write(2, (t >> 16) & 0xff);
+    EEPROM.write(3, (t >> 24) & 0xff);
+    Serial_print(PSTR("eeprom write: "));
+    Serial.println(t);
+}
+
+static void setDisplay(long v)
+{
+    Display[3] = 0x30 + (v % 10);
+    Display[2] = 0x30 + ((v/10) % 10);
+    Display[1] = 0x30 + ((v/100) % 10);
+    Display[0] = 0x30 + ((v/1000) % 10);
+}
+
+void
+setup()
+{
+    // disable TWI, Timer1, SPI and USART0
+    //PRR &= (1<<PRTWI) | (1<<PRTIM1) | (1<<PRSPI);// | (1 << PRUSART0);
+
+    // Enable the internal 1.1V AREF (requires 10nF cap to GND)
+    analogReference(INTERNAL);
+    
+#if HAVE_SHIFT_REGISTER
+    pinMode(SHIFTREG_DATA, OUTPUT);
+    pinMode(SHIFTREG_LATCH, OUTPUT);
+    pinMode(SHIFTREG_CLOCK, OUTPUT);
+    pinMode(BUTTON_L, INPUT);
+    pinMode(BUTTON_R, INPUT);
+    pinMode(LED_PIN, OUTPUT);
+    digitalWrite(LED_PIN, HIGH);
+#else
+    pinMode(SEG_A, OUTPUT);
+    pinMode(SEG_B, OUTPUT);
+    pinMode(SEG_C, OUTPUT);
+    pinMode(SEG_D, OUTPUT);
+    pinMode(SEG_E, OUTPUT);
+    pinMode(SEG_F, OUTPUT);
+    pinMode(SEG_G, OUTPUT);
+    pinMode(SEG_DP, OUTPUT);
+#endif
+    pinMode(SEL_1, OUTPUT);
+    pinMode(SEL_2, OUTPUT);
+    pinMode(SEL_3, OUTPUT);
+    pinMode(SEL_4, OUTPUT);
+    
+    Serial.begin(9600);
+    Serial_print(PSTR("Slide Warmer - Copyright (c) 2013 Pat Thoyts\r\n"));
+    current_digit = 0;
+    memset(Display, 0x30, sizeof(Display));
+    target_temp = stored_target_read();
+    setDisplay(target_temp);
+    
+    Timer2Init();
+}
+
+static long getTemperature()
+{
+    long v;
+    v = analogRead(LM35_PIN);
+    Serial.print(v);
+    v = v * 10000L / 931L;
+    Serial.print(" ");
+    return v;
+}
+
+static void setState(byte state)
+{
+    display_state = state;
+    Serial_print(PSTR("state: "));
+    Serial.println(state);
+}
+
+static void incrementTargetTemp(int delta)
+{
+    target_temp += delta;
+    if (target_temp < 1000) target_temp = 1000;
+    if (target_temp > 11000) target_temp = 11000;
+    setDisplay(target_temp);
+    Serial_print(PSTR("target: "));
+    Serial.println(target_temp);
+}
+
+MilliTimer timer0, timer1, timer2, timer3;
+bool keypress_delay = false;
+
+void
+loop()
+{
+    long v = 0, t[NUMSAMPLES];
+    int n;
+
+    if (timer1.poll(100)) {
+        bool left_button = digitalRead(BUTTON_L) != HIGH;
+        bool right_button = digitalRead(BUTTON_R) != HIGH;
+        switch (display_state)
+        {
+            case STATE_NORMAL:
+                if (left_button || right_button) {
+                    setState(STATE_USERPRESS);
+                    timer2.set(1000);
+                }
+                break;
+            case STATE_USERPRESS:
+                if (timer2.poll()) {
+                    if (left_button || right_button) {
+                        setState(STATE_FLASHING);
+                        timer2.set(5000);
+                        keypress_delay = true;
+                        setDisplay(target_temp);
+                        last_target = target_temp;
+                    }
+                    else {
+                        setState(STATE_NORMAL);
+                    }
+                }
+                break;
+            case STATE_FLASHING:
+                // avoid accepting presses for the first second
+                if (!keypress_delay) {
+                    if (left_button)
+                        incrementTargetTemp(-100);
+                    if (right_button)
+                        incrementTargetTemp(100);
+                }
+                if (left_button || right_button) // reset timeout
+                    timer2.set(5000);
+                if (keypress_delay) {
+                    if (timer2.remaining() < 4000)
+                        keypress_delay = false;
+                }
+                if (timer2.poll()) {
+                    setState(STATE_NORMAL);
+                    if (target_temp != last_target) {
+                        stored_target_write(target_temp);
+                    }
+                }
+                break;
+        }
+    }
+
+    if (timer0.poll(1000)) {
+        if (display_state != STATE_FLASHING) {
+            Serial.print(millis());
+            Serial.print(" ");
+            for (n = 0; n < NUMSAMPLES; ++n) {
+                t[n] = getTemperature();
+                delay(10);
+            }
+            for (n = 0 ; n < NUMSAMPLES; ++n) {
+                v += t[n];
+            }
+            v /= NUMSAMPLES;
+            Serial.println(v);
+            setDisplay(v);
+        }
+    }
+}