diff --git a/simpleclock.ino b/simpleclock.ino index 51c9bda..4547d75 100644 --- a/simpleclock.ino +++ b/simpleclock.ino @@ -11,6 +11,9 @@ #include #include +#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) +#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) + DS1307 clock; SevSeg Display; @@ -20,25 +23,32 @@ const long adjust_interval = 200; // delay between consecutive number increments const long bounce_delay = 20; // contact bounce compensation const int hrs_btn = A0; // put hour adjusting button on A0 lane const int min_btn = A1; // put minute adjusting button on A1 lane -bool btn_pushed = false; // is button pushed? -unsigned long currentPushTimer; +volatile bool btn_pushed = false; // is button pushed? unsigned long previousPushTimer = 0; -uint8_t minutes; -uint8_t hours; /* dot blinking stuff */ -unsigned long currentMillis; -unsigned long previousMillis = 0; -const long interval = 500; -bool dot = true; +volatile bool dot = true; /* time inaccuracy compensation flag */ -bool compflag = true; -const uint8_t timeSkew = 6; // assume that clock runs faster than should -bool timeSkewEven = false; // comp. 1 additional second per 2 days +volatile bool compflag1 = true; +volatile bool compflag2 = false; +const uint8_t timeSkew = 8; // assume that clock runs faster than should +volatile bool timeSkewEven = false; // comp. 1 additional second per 2 days /* ambient light compensation aka night mode */ const int photoPin = A2; +volatile bool measureAmbLight = true; +unsigned int ambLight = 0; +unsigned long previousLightTimer = 0; +const long refresh_interval = 40; // refresh rate 25Hz + +/* time vars */ +volatile uint8_t mysecond; +volatile uint8_t myminute; +volatile uint8_t myhour; + +/* rtc sync flag */ +volatile bool rtcsync = true; void setup() { @@ -49,7 +59,7 @@ void setup() byte segmentPins[] = {2, 3, 4, 5, 6, 7, 8, 13}; bool resistorsOnSegments = true; // 8 resistors, 330 Ohms each /* updateWithDelays defaults to false and it is recommended value, - * but i was unable to make my display to change brightness with + * but i was unable to make my display to change brightness notably with * recommended value, so i have to use updateWithDelays=true, * in this case brightness gets changed. */ @@ -80,25 +90,96 @@ void setup() pinMode(hrs_btn, INPUT_PULLUP); pinMode(min_btn, INPUT_PULLUP); +/* Timer stuff + * timer0 being used in millis() micros() delay() etc, it has 8 bit resolution + * timer2 being used in analogWrite() tone() notone(), it has 8 bit resolution + * timer1 being used in servo lib has 16 bit resolution + * We'll use timer1, since we have no mechanical parts and don't need servo lib + */ + // initialize timer1 + noInterrupts(); // disable all interrupts + TCCR1A = 0; + TCCR1B = 0; + + TCNT1 = 34286; // preload timer 65536-16MHz/256/2Hz + TCCR1B |= (1 << CS12); // 256 prescaler + TIMSK1 |= (1 << TOIE1); // enable timer overflow interrupt + interrupts(); // enable all interrupts + +/* increase sampling rate of analogRead() + * by setting an ADC prescale from 128 to 16, so it takes less time to read + * sensor + */ + sbi(ADCSRA, ADPS2); + cbi(ADCSRA, ADPS1); + cbi(ADCSRA, ADPS0); + // setup ambient brigtness sensor pinMode(photoPin, INPUT); + ambLight = analogRead(photoPin); +} + +ISR(TIMER1_OVF_vect) +{ +/* Feed clock twice per second */ + TCNT1 = 34286; // preload timer + // do not read rtc if time beng adjusted by buttons + (btn_pushed) ? rtcsync : rtcsync = true; + +/* Also switch dot */ + if (dot) + dot = false; + else + dot = true; + +/* time compensation magic */ + if ((myhour == 21) and (myminute == 21) and (mysecond == 21) + and (compflag2)) { + compflag1 = true; + (timeSkewEven) ? timeSkewEven = false : timeSkewEven; + } + + if ((myhour == 21) and (myminute == 21) and (mysecond == 23)) { + compflag1 = false; + compflag2 = true; + } } void loop() { - currentMillis = millis(); - currentPushTimer = currentMillis; + unsigned long currentMillis = millis(); char str[4]; float d; - unsigned int ambLight = analogRead(photoPin); - clock.getTime(); +/* + * Let's implement time compenstion for individual rtc module time + * charachteristics. In in my case quartz resonator as inaccurate as + * about +8.5 seconds per 24 hours + */ + if (compflag1 && compflag2) { + if (timeSkewEven) { + clock.fillByHMS(myhour, myminute, (mysecond - timeSkew - 1)); + } else { + clock.fillByHMS(myhour, myminute, (mysecond - timeSkew)); + } + + clock.setTime(); + compflag2 = false; + } + + if (rtcsync) { + clock.getTime(); + mysecond = clock.second; + myminute = clock.minute; + myhour = clock.hour; + rtcsync = false; + } // push button magic if ((!btn_pushed) and ((digitalRead(hrs_btn) == LOW) or (digitalRead(min_btn) == LOW))) { - if (currentPushTimer - previousPushTimer >= initial_delay) { - previousPushTimer = currentPushTimer; + if (currentMillis - previousPushTimer >= initial_delay) { + previousPushTimer = currentMillis; btn_pushed = true; } } @@ -106,112 +187,95 @@ void loop() if (btn_pushed and (digitalRead(hrs_btn) != LOW) and (digitalRead(min_btn) != LOW)) { // compensate possible contact bounce with delay - if (currentPushTimer - previousPushTimer >= bounce_delay) { + if (currentMillis - previousPushTimer >= bounce_delay) { btn_pushed = false; } } if (btn_pushed) { - if (currentPushTimer - previousPushTimer >= adjust_interval) { - previousPushTimer = currentPushTimer; + if (currentMillis - previousPushTimer >= adjust_interval) { + previousPushTimer = currentMillis; if (digitalRead(hrs_btn) == LOW) { - hours = clock.hour + 1; - (hours > 23) ? hours = 0 : hours; - clock.fillByHMS(hours, clock.minute, 0); + myhour++; + (myhour > 23) ? myhour = 0 : myhour; + clock.fillByHMS(myhour, myminute, 0); clock.setTime(); } if (digitalRead(min_btn) == LOW) { - minutes = clock.minute + 1; - (minutes > 59) ? minutes = 0 : minutes; - clock.fillByHMS(clock.hour, minutes, 0); + myminute++; + (myminute > 59) ? myminute = 0 : myminute; + clock.fillByHMS(myhour, myminute, 0); clock.setTime(); } } } - if (currentMillis - previousMillis >= interval) { - previousMillis = currentMillis; + if ((currentMillis - previousLightTimer >= refresh_interval) or + (previousLightTimer > currentMillis)) { // millis() reset every ~49 days + previousLightTimer = currentMillis; + ambLight = analogRead(photoPin); - if (dot) - dot = false; - else - dot = true; - } - - /* - * Let's implement time compenstion for individual rtc module time - * charachteristics. In in my case quartz resonator as inaccurate as - * +/- about 6.5 seconds per 24 hours, maybe its require more EMI - * shielding? - */ - - if ((clock.hour == 21) and (clock.minute == 21) and - (clock.second == 21) and (compflag)) { - - if (timeSkewEven) { - timeSkewEven = false; - clock.fillByHMS(clock.hour, clock.minute, - (clock.second - timeSkew - 1)); - } else { - timeSkewEven = true; - clock.fillByHMS(clock.hour, clock.minute, - (clock.second - timeSkew)); +/* Ambient light compensation + * ambLight can be in range of 0..1023, if it 910+, it has point to use + * fullbright. + * Use table comparsion (not function of ambLight) because of non-linear + * charachteristics of led indicator, that cannot be described via simple + * formula. + */ + if (ambLight >= 910) { + Display.setBrightness(100); + } else if ((ambLight < 900) and (ambLight >= 860)) { + Display.setBrightness(75); + } else if ((ambLight < 850) and (ambLight >= 810)) { + Display.setBrightness(45); + } else if ((ambLight < 800) and (ambLight >= 760)) { + Display.setBrightness(25); + } else if ((ambLight < 750) and (ambLight >= 710)) { + Display.setBrightness(18); + } else if ((ambLight < 700) and (ambLight >= 660)) { + Display.setBrightness(12); + } else if ((ambLight < 650) and (ambLight >= 610)) { + Display.setBrightness(10); + } else if ((ambLight < 600) and (ambLight >= 560)) { + Display.setBrightness(7); + } else if ((ambLight < 550) and (ambLight >= 410)) { + Display.setBrightness(4); + } else if ((ambLight < 400) and (ambLight >= 360)) { + Display.setBrightness(0); + } else if ((ambLight < 350) and (ambLight >= 310)) { + Display.setBrightness(-3); + } else if ((ambLight < 300) and (ambLight >= 260)) { + Display.setBrightness(-5); + } else if ((ambLight < 250) and (ambLight >= 210)) { + Display.setBrightness(-7); + } else if ((ambLight < 200) and (ambLight >= 160)) { + Display.setBrightness(-9); + } else if ((ambLight < 150) and (ambLight >= 110)) { + Display.setBrightness(-12); + } else if (ambLight < 100) { + Display.setBrightness(-15); } - clock.setTime(); - compflag = false; - } - - if ((clock.hour == 21) and (clock.minute == 21) and - (clock.second == 23) and (!compflag)) { - compflag = true; - } - - // make blinking dot - if (clock.hour > 0) { - if (dot) { - Display.setNumber((clock.hour * 100 + clock.minute), 2); + // make blinking dot + if (myhour > 0) { + if (dot) { + Display.setNumber((myhour * 100 + myminute), 2); + } else { + Display.setNumber((myhour * 100 + myminute)); + } } else { - Display.setNumber((clock.hour * 100 + clock.minute)); + /* 0 o'clock is an exception, we don't want to see "00.00", we + want see "0.00" or "0 00", so we have to use this "hack" */ + if (dot) { + d = float(myminute) / 100; + Display.setNumber(d, 2); + } else { + sprintf(str, "%2d%02d", myhour, myminute); + Display.setChars(str); + } } - } else { - /* 0 o'clock is an exception, we don't want to see "00.00", we - want see "0.00" or "0 00", so we have to use this "hack" */ - if (dot) { - d = float(clock.minute) / 100; - Display.setNumber(d, 2); - } else { - sprintf(str, "%2d%02d", clock.hour, clock.minute); - Display.setChars(str); - } - } - - /* ambient light compensation - * ambLight can be in range of 0..1023, if it 800, it has point to use - * fullbright - */ - if (ambLight >= 940) { - Display.setBrightness(100); - } else if ((ambLight < 900) and (ambLight >= 840)) { - Display.setBrightness(75); - } else if ((ambLight < 800) and (ambLight >= 735)) { - Display.setBrightness(40); - } else if ((ambLight < 700) and (ambLight >= 635)) { - Display.setBrightness(15); - } else if ((ambLight < 600) and (ambLight >= 530)) { - Display.setBrightness(12); - } else if ((ambLight < 500) and (ambLight >= 430)) { - Display.setBrightness(10); - } else if ((ambLight < 400) and (ambLight >= 325)) { - Display.setBrightness(8); - } else if ((ambLight < 300) and (ambLight >= 220)) { - Display.setBrightness(6); - } else if ((ambLight < 200) and (ambLight >= 110)) { - Display.setBrightness(4); - } else if (ambLight < 100) { - Display.setBrightness(3); } Display.refreshDisplay();