--- /dev/null
+/* -*- 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);
+ }
+ }
+}