2013-08-29 新作「無線LAN対応NTP時計」はこちら
AVRマイコンATMEGA328PとATTINY2313、イーサネットコントローラENC28J60を使用した、大型デジタル床置き時計です。
ヤフオクで購入した、大型青色LEDを搭載した電波時計が、あまりにも不甲斐ないのです。
そこで、7セグメントLEDだけ再利用して、ネットワーク(NTPサーバ)から時刻を取得する、正確な時計を作ってみました。
中国製です。
ふたを開けたところ。
7セグメントLEDを外して、基板に配置しました。
年月日時分秒を表示するための基板を5つに分けます。
制御用マイコンはATTINY2313を使用します。
マイコンはATMEGA328P、イーサネットコントローラはENC28J60を使用します。
木製(MDF板)の土台に配置します。
時計制御基板を土台の背面に取り付けます。
LED表示基板と配線します。
ATMEGA328Pのから各表示基板へ
ATTINY2313(表示基板)のSPI入力部分
前面の様子。
アクリル板のフロントパネルを取り付けて完成です。
時計制御基板(ATMEGA328P)用と、LED表示制御基板(ATTINY2313)用に分かれています。さらに、LED表示制御基板は、年、月日、時、分、秒の5種類あります。
ntpclock.hex | 時計制御基板(ATMEGA328P)用 | インターネット接続し、時刻を取得する。 また、各表示基板に表示すべき数字情報を送信する。 |
clock_led_year.hex | LED表示制御基板(ATTINY2313)用その1 | 「年」を表示する。 |
clock_led_day.hex | LED表示制御基板(ATTINY2313)用その2 | 「月日」を表示する。 |
clock_led_hour.hex | LED表示制御基板(ATTINY2313)用その3 | 「時」を表示する。 |
clock_led_min.hex | LED表示制御基板(ATTINY2313)用その4 | 「分」を表示する。 |
clock_led_sec.hex | LED表示制御基板(ATTINY2313)用その5 | 「秒」を表示する。 |
ファームウェアntpclock.hexを書き込みます。ヒューズビットは、Low=0xe6、High=0xd9、Ext=0xffを設定します。
クロック周波数は19.6608MHzです。外付けの水晶発振子を使用します。
ファームウェアは、各エレメント毎に、clock_led_*.hexを書き込みます。ヒューズビットは、Low=0xe4を設定します。
クロック周波数は8MHzです。チップ内蔵のオシレータを利用します。
ATMEGA328PとENC28J60とは、マイコン搭載のSPI機能で通信します。ATMEGA328Pと各ATTINY2313とは、ソフトウェア制御によるSPIで通信します。
1時間に2回(毎時29分30秒と59分30秒)時刻の更新をします。
ATMEGA328Pは、プログラムメモリが32kB、データ用SRAMが2kBしかありませんので、TCP/IPの全機能を実装するには小さすぎます。以下のプロトコルに限定して実装しました。データ通信はUDPのみで、TCPには対応していません。また、パケットサイズは、イーサネットの規定では最大1518バイトですが、本機では、最大384バイトに制限しています。
MACアドレスは、本来、世界にただ一つになるように設定する必要がありますが、同一LAN内で重複しなければいいと考えて、とりあえずFE:FF:FF:00:00:01を設定しています。この装置を同一LAN内に複数設置する場合は、MACアドレスを個別に設定する必要があります。本機はEEPROM等を搭載しておらず、ATMEGA328Pのファームウェアに定数で書き込んでありますので、変更するには再コンパイルが必要です。
パケットキャプチャ(Wireshark)で通信内容を表示させてみました。
NTPサーバから送られてくるデータは、1900年1月1日(UTC)からの積算秒数で表されます。これをユリウス通日に変換し、タイムゾーン値を加算した後、グレゴリオ暦に変換します。
※ 参考:「プログラマのためのSQL 第4版」 第38章:ユリウス日付
void convert_cjd_to_ymd(unsigned long j, int *year, int *month, int *day)
{
int y, m, d;
y = (j * 4 + 128179) / 146097;
d = (j * 4 - y * 146097 + 128179) / 4 * 4 + 3;
j = d / 1461;
d = (d - j * 1461) / 4 * 5 + 2;
m = d / 153;
d = (d - m * 153) / 5 + 1;
y = (y - 48) * 100 + j;
if (m < 10) {
m += 3;
} else {
m -= 9;
y++;
}
*year = y;
*month = m;
*day = d;
}
ATMEGA328PからATTINY2313へ送信するデータのプロトコルを説明します。
ATMEGA328PのPORT.D.0がクロック、PORT.D.1がデータです。
表示する数字の桁位置と文字を1バイトのデータで送信します。
クロックの推奨周波数は100kHzです。速すぎると、ATTINY2313側で受信できません。遅すぎると、タイムアウトして、これも受信できません。タイムアウトさせないための最低周波数は1kHzです。
ビット | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
内容 | 桁位置 | 数字 |
桁位置は0~13、数字は0~9です。
0 | 秒の1の位 |
1 | 秒の10の位 |
2 | 分の1の位 |
3 | 分の10の位 |
4 | 時の1の位 |
5 | 時の10の位 |
6 | 日の1の位 |
7 | 日の10の位 |
8 | 月の1の位 |
9 | 月の10の位 |
10 | 年の1の位 |
11 | 年の10の位 |
12 | 年の100の位 |
13 | 年の1000の位 |
各ATTINY2313は、自分が表示すべき桁位置のデータが送られてきたら数字を更新して、それ以外のデータは無視します。
安定化された5VのスイッチングACアダプタを使用します。
LED表示回路は5Vで動作します。
ATMEGA328PとENC28J60は3.3Vですが、手持ち部品の中に、適当な3.3Vのレギュレータが無かったので、ダイオード2本を直列につないで3.4V程度まで降下させています。
電源投入直後の数秒間は770mA程度流れます。NTPサーバから時刻の取得が成功し、平常動作になると670mAくらいで安定します。ACアダプタは1A以上のものを使用します。
ENC28J60のデータシートにはVCAP端子に低ESRなコンデンサを使うよう書かれていましたので、三洋OS-CONを使用しました。実験時には、安価な汎用品を使用しましたが、それでも正常に動いていたようですので、あまり気にしなくてもいいのかもしれません。