WiFiでNTPサーバーに時刻同期するNTP時計の製作 2019.11.15

    1.はじめに
     製作したNTP時計。7セグ風の数字フォントは自作した。

     電子工作を趣味として続けていると、いつの間にか不要な電子パーツがたまってきます。

     トラ技誌等の付録基板やマイコン、試作回路基板やLCD、さらには学習用で購入したICやモジュールなどです。

     今回、そんなパーツを有効活用すべくWiFiでNTPサーバーにアクセスし、時刻同期を行うNTP時計を製作してみました。


    2.使用したパーツ

    2-1. PIC

     まずは、PICマイコンです。こちらは、USBを使ったある回路の試作基板をそのまま使うことにしました。

     搭載されているPICは、PIC32MX250F128Bで、128KBのROM容量があり時計の製作には十分です。

     基板には、+3.3Vのレギュレータや、デカップリングのコンデンサー、セラミック発振子、表示用LEDが搭載済みで、PICの各端子も全て基板外に引き出されていますので+5Vのみ接続すれば動かすことが出来ます。


    2-2.WiFiモジュール

     WiFiアクセス用には、秋月電子で2016年9月に何の目的もなく購入し、使わずじまいであったESP-WROOM-02を使います。

     このモジュールを使ってみるのは今回が初めてですが、秋月での発売(2015/09/24)からは4年も経過しているのでネット上には参考となる使用例もたくさんあり、簡単に動かすことが出来ました。


    2-3.グラフィック型LCD

     次に時計の時刻表示部です。7セグメントLEDがいくつか不要パーツとして手元にあったのですが、ドライブ回路を作るのも手間なのでLCDを使うことにしました。

     LCDはキャラクタ型のものがいくつかあったのですが、時刻表示は7セグ数字のような表示にしたくてグラフィックLCDを使うことにしました。

     そのグラフィックLCDは、大阪日本橋のデジットで、当時500円で購入したDMF-51026NY-LY(datasheet)というものです。

     横160ドット、縦64ドットありますので、好きな文字を表示することができます。購入したのは、2011年7月と随分前になります。

     dsPIC30F3012を使って表示テストをしたままで、その後パーツ箱で眠ったままでした。確か、ライン表示や5x7フォントや8x16フォントなどの文字表示ルーチンを作成したはずです。

     このLCDは、ちょっと癖があります。表示用のタイミングパルスを全てマイコンから与えてやらないと表示しません。表示用のタイミングパルスの生成処理に追われていては他の仕事が出来ません。

     難題は、液晶のドライブに-8Vから-15Vの範囲の負電圧が必要になることです。

     この負電圧をどのように回路準備するかが課題の一つでもあります。8年前のテストでは、9Vの006P電池で負電圧を与えたと記憶しています。今回は電池を使わない方法を考えます。


    2-4.計時用クロック源

     NTPサーバーに同期するといっても、時計モジュール自体を装置内に持つ必要があります。

     使用するマイコンには、RTCモジュールが内蔵されています。これを使うにはセカンダリオシレータとして32.768KHZの水晶振動子を取り付ける必要があります。ところが基板には追加で取り付けるスペースはありません。

     そこで、PICのクロックを適宜分周して1秒間隔のタイマー割り込みを発生させ、時刻、日付を計時する方法を考えました。秒以下の時刻はクロックの分周回路の現在値を読み取ることで判ります。

     使用するPICのクロックは40MHZです。これを32ビットのタイマーでカウントするなら、タイマーの値が、0x02625A00(10進数で40,000,000)に達する毎に割り込みを発生させ、秒未満の値はこのタイマーの値を読むことになります。

     一方、NTPサーバーからは、1900年1月1日0時0分0秒を起点とした経過秒数が64ビットで送られてきます。上位32ビットで整数部、下位32ビットで小数点以下を表しています。固定小数点です。

     例えば経過秒数が 10.75 秒であった場合、 0x0000000AC0000000 となります。下位32ビットの上位ビットは、0.5秒を示し、次のビットは、0.25秒を示します。

     マイコン内で小数点以下の秒数を2進でカウントするレジスタでもあれば、NTPサーバーから取得した値でレジスタを上書きすれば、秒以下の時刻同期が容易に取れるようになります。

     先に検討したPICのクロックの分周では、固定少数点に合わせるには、タイマーレジスタの値に何らかの補正処理をしないといけませんが、外部から32.768KHZのクロックを入力して、それをカウントすれば、補正が不要な2進カウントを容易に実現出来ます。

     このクロック源として、測定器の試作基板から取り外したSEIKO EPSONのRTCモジュールRTC-8564NBを使うことにしました。

     RTC機能は全く使わずに、電源が投入されれば自動的に出力されてくる32.768KHZのクロック出力だけを使います。


    3.製 作

    3-1.ブレッドボードでの試作

     主要なパーツが出揃いましたので、PICのプログラムの開発・動作検証用としてブレッドボードにこれらのパーツを取り付けます。

     LCDは15ピンのPHコネクタでケーブル接続しますが、ケーブルの片側は芯線をそのままブレッドボードの穴に差し込みました。

     LCDへの負電圧供給は、作業開始時は006Pの電池を使いましたが、検証の過程で、PICマイコンによる電圧変換回路を作成し、そちらに置き換えました。

     トランジスタ技術SPECIAL No137 今すぐ作れる!今すぐ動く!実用アナログ回路事典250 に「汎用ロジックICでチョッコと負電圧を作る回路」という見出しで+5Vから負電圧を作る回路が掲載されていました。

     回路例では、TC74AC14Fが使われていましたが、手持ちもなく、単にパルスを発振させるだけですので dsPIC30F3012で代用して見ました。これがうまくいきました。課題の一つであった負電圧の生成はこれで解決です。

     自宅にはサーバーを設置しています。宅内には、WiFiAPを設置していますので自宅内のどこからでもサーバーにアクセスすることができます。自宅内サーバーはNTPサーバーとしても動作しています。

     LCDに表示する7セグ風のフォントデータは、エクセルにて手書きで作成しました。少し傾斜したフォントにしたかったのですが、一文字を横16ドット、縦24ドットで表現するには無理な話で直立のフォントしか作成できませんでした。

    7seg_like_font.xls


    3-2.ユニバーサル基板での製作

     ブレッドボードでの試作で時計の表示、NTPサーバーとの時刻同期がうまく出来るようになりましたので、ユニバーサル基板にパーツを取り付け直します。 この時、負電圧作成用のPICは、8ピンのPIC12F629に置き換えました。

     秋月電子で販売しているB基板(95mmx72mm)に収まるようにパーツを配置しました。LCDとの接続は、基板上にXHコネクタを配し、全てケーブルによるコネクタ接続としました。 コネクタを採用することですっきりとした出来上がりになりました。

     基板むき出しのままでは設置場所も限定されてしまいます。自宅内のどこにでも設置できるよう、3Dプリンタあるいはプラ板で適当なケースを作成してみようと思っています。

    7セグ風フォントもうまく表示できています。 LCDはケーブルで接続しています。負電圧は8ピンPICで作りました。 モジュール類はICソケットに差し込みましたので抜け防止にワイヤで止めています。

    4.回路図


     各パーツ間の配線等、回路は左図のようになります。

     マイコン基板上の部品については記載を省略しています。

     またRA0、RA1の2ピンの引き出し位置が変更されています。回路図の間違いではありません。

    5.プログラム

     作成したPICのプログラムについて簡単に説明しておきます。詳細はソースコードをご覧ください。

    5-1.wifi.c

     wifi_putstr()にてATコマンドを送信しています。

     モジュールからの応答は、wifi_wait()で受信します。wifi_wait()の第1引数は、受信期待文字列です。第2引数は受信タイムアウト検出用タイマー値となっています。

     ATコマンドのエコーを禁止していませんので、送信したコマンドのエコーを受信する処理が随所に入っていてちょっとうざいです。

     スルーモード以降のバイナリデータの送受信(NTPリクエストパケットの送信、NTPパケットの受信)は、wifi_send(), wifi_rec()を使います。


    5-2. dmf51026.c

     LCDの表示用タイミングパルスは、TMR3の割り込み処理で生成しています。

     割り込みは、2.35us毎に発生させています。これで仕様書どおりのタイミングです。リフレッシュレートは80Hzとなりチラつきも無く良好な表示が得られています。

     表示用データは、配列 vram[1280(=160*64/8)]に格納しておきます。

     LCDの表示したい位置に該当するvram[]の内容を変更するだけで、自動的に割り込み処理によってLCDに表示できます。

     その他の関数は、LCDにラインを引いたり、文字フォントを表示するものです。全て vram[] を更新しているだけです。

     英数、カナの表示用フォントは、font6x8.h, font8x16.h, font16x24.h の各ヘッダファイルに格納しています。

     font16x24.hは、7セグ風数字フォントです。0から9までの数字とコロンとスラッシュの12文字だけです。


    5-3. clock.c

     時計の処理です。32.768KHZのクロックをタイマー1でカウントします。TMR1レジスタが 0x7FFFを越えるとタイマー割り込みが発生します。

     割り込み処理では、clock_up_count()を呼び出して、1秒カウントアップの処理を行うとともに、clock_chg_flg をセットし、1秒更新を通知します。

     timer.c の中の10msの割り込み処理が、 clock_chg_flg を監視しており、更新があれば、clock_display()を呼び出して、LCDに時刻を表示します。

     NTPサーバーから取得した秒の値は date.seconds に、秒未満の値は、date.fractions に格納されて、seconds2date()関数が呼ばれます。

     seconds2date()関数では、秒数から日付・時刻を求めています。

     set_clock()関数にて、内部時計の日付・時刻、秒未満の値を更新します。


    5-4. network.c

     NTPサーバーに時刻リクエストのパケットを送信し、時刻パケットを受信する処理です。

     ntp_command()関数が main.c 内から5分毎に呼び出されます。

     ntp_command()関数は、get_ntp_time()を呼びだして、受信した時刻を使って、 second2date(), set_clock()を順次呼び出し、時刻同期を行います。

     get_ntp_time()関数では、NTPサーバーから受信した時刻をそのまま出力するのではなく、パケットの伝送遅延やPIC内部での処理時間、LCDの表示遅延時間の補正を行っています。


    5-5.その他のソースコード

     残りのソースコードです。説明は省略します。

      delay.c ...1mse単位の遅延処理です。
      main.c ...メイン処理です。
      timer.c ...10mseのtimerです。
      uart1.c ...割り込みで送受信しています。
      uart2.c ...割り込みで送受信しています。
      dccon.asm ...PIC12F629の電圧変換用パルス発生処理です。