OBONO’s Diary

へっぽこプログラマの戯言

万年カレンダー

先日勢いで購入した 128×128 TFT 液晶を Arduino Nano 互換機に繋いで、万年カレンダーを作ることにした。

というわけで、とりあえず画面に時刻やカレンダーを表示できるようにはなったので、後は時間経過とともに時刻を進める実装を書かなくてはならない。
電力消費を抑えるため、画面更新時以外は基本的にスリープさせて、Watchdog 割り込みを利用して秒を刻む方針でコードを書いてみた。

#include <arduino.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

volatile int wdCount = 0;

void setup()
{
    wdt_reset();
    cli();
    MCUSR  = 0; // WDRF reset
    WDTCSR |= 0b00011000; // WDCE WDE set
    WDTCSR =  0b01000000 | 0b00000110; // WDIE set | WDP set scale 1sec
    sei();
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
}

void loop() {
    while (wdCount--) { 
        // do something
    }
    sleep();
}

static void sleep(void)
{
    cli();
    sleep_enable();
    sleep_bod_disable();
    sei();
    sleep_cpu();
    /* wake up here */
    sleep_disable();
}

ISR(WDT_vect)
{
    wdCount++;
}

…が、タイマーの精度があまりにも悪く、1時間経過したころには4,5分もズレてしまう。これじゃあ、時計としては致命的。
仕方がないので、消費電力の事は気にせず、無限ループ内で適当に delay() するように方針変更。諸般の事情で、50ms 毎にループを回してる。

#include <arduino.h>

#define FPS 20
#define MILLIS_PER_FRAME (1000 / (FPS))

int             frames;
long            targetTime;

void setup()
{
    frames = 0;
    targetTime = millis();
}

void loop()
{
    while (frames >= FPS) {
        // do something
        frames -= FPS;
    }
    targetTime += MILLIS_PER_FRAME;
    long delayTime = targetTime - millis();
    if (!bitRead(delayTime, 31)) delay(delayTime);
    frames++;
}

これだと、割と良さそうな精度で時間が進んで行ってくれた。実際、精度がどの程度なのか、丸一日動かしてみて様子を見てみるか。
今日はここまで。