PIC16F886・Head Up Display 電波時計の製作 (2011/01/24〜2011/02/18)

(Last update:2015/10/10)


こいつがマナ板に上がった
市販の電波時計
開腹してみたところ。
受信モジュールを
取り出せそうだ(ラッキー♪)
摘出した受信モジュール。
一般的な 5ピン仕様だった
まずはパルス判定部分から
開発開始
これぐらい綺麗な信号なら
処理も簡単だが・・・
受信状態が悪くなってくると
信号が割れ始める。
開発最終段階 制御部表。
普段スイッチは使用しないので、
基板直に付け。
制御部裏 表示部表。

写真は CRD 15mA x 1 の時のもの。
最終的には 15mA x 2 のパラ接続で
30mA にした。

LED 周りは不要な映り込みを
防ぐため黒塗りにした。
表示部裏 受信部
モジュール裏に絶縁テープを
貼って SOT変換基板上に
トランジスタと抵抗 x 2 を実装。
受信部は秋月の単四電池
ボックスに入れてみた
受信部は表示部から
離して設置
HUD 速度計の隣に設置。

(9:28 表示だがダイナミック点灯
のため一部数字が消えて写っている)
回路図 参考配線レイアウト
(レイアウト作成には PaaSを使用)
動作の様子

製作の動機:

 市販用の車載電波時計(写真、YAC PZ-486)を購入したのだが、電池が 1ヶ月と持たない @o@。一応太陽電池が付いているにもかかわらず、何度電池を換えてもすぐに電池切れになってしまうヘタレっぷり(商品としてどうよ)。

 おまけに液晶の視野角が狭く、フラストレーションが溜まる一方だったので開腹したところ、受信部を摘出できることが判明したため、自前で電波時計に仕上げることに。


仕様:

 ありきたりのものを作っても面白みが無いので、以下の仕様とする。

 ・時・分表示に限定し、デコードを高速化
 ・表示は 7 セグ LED
 ・Head Up Display (HUD) 表示
 ・PWM ディマー機能実装
 ・電源は車の常時電源
 ・7 セグ LED は停車時表示 OFF、アクセサリ電源で ON
 ・停車時は常時受信〜校正
 ・電波が拾えなくても自立した時計として機能


今回の狙い:

 そもそも温度変化の激しい車内ではクォーツ時計は狂いやすい(クリスタルの発振周波数がズレ易いから)。

 よって「車に電波時計」というのは非常に理にかなっているのだが、市販の電池式電波時計(車載品を含む)は電池寿命を延ばすため一日数回(ひどいものだと一日一回!)しか電波を受信しない。これでは受信時刻にたまたま受信状態の悪いところにいると、せっかくの電波時計がただの精度の悪い時計になってしまう可能性大だ(爆)。

 どうせ自作するならケチなことはいわず、常時受信〜校正として電波時計のポテンシャルをぐっと引き出してやろう ^^;

 また一連の前作で培った Head Up Display ノウハウ (?) を投入し、さらにオリジナリティを追求してみた.。少なくとも製作時点(2011/02)ではググってみても同一作品は見当たらなかったので、そこそこのインパクトが期待できるのではなかろうか。


ハードウエア:

【受信モジュールまわり】

 まずは受信モジュール(写真参照)を摘出する前にオシロで信号を確認。その結果、5つのピンは GND, TCO(負論理), VCC(3V), PON (L active), 40/60 (L=40KHz) と推定された。

 先人の調査によると、市販の電波時計の多くは上記 5信号が引き出されたモジュールが使われているようなので、他の電波時計から取り出したモジュールでも使える可能性は高い(電源電圧や TCO の論理レベルは要注意)。

 今回のモジュールは TCO が負論理のためトランジスタで論理反転して PIC に入力する。(最初オシロで波形を見たときは負論理であることにしばらく気付かなかった。我ながら修行が足りマセン・・・ orz)。トランジスタは実装の都合上チップ品を使用したが、当然 2SC1815 のような汎用品で十分。

 なおこの電波時計モジュール、電源を投入して 1分半ほど経たないと TCO 信号を出してくれない。おそらく電波をある程度受信してオートゲインコントロールしていると思うのだが、おかげで電波状況変化に素早く追従できず、バーアンテナの向きを変えたりするだけでしばらく TCO 信号が出てこなくなる。

 もともと据え置き時計用の復調モジュールを車載用に流用しているあたり、もともとの商品にムリがある気がするが、この復調モジュールを使う限り停車時しかうまく信号を受信できないと思われる。


【電源】

 電波時計モジュールはもともと 3V のコイン電池で動いていたものなので、PIC ともども 3.3V の三端子レギュレーターで降圧して電源を供給。


【7 セグ LED まわり】

 7 セグ LED には「世界最高輝度チップ採用」(By 秋月)を誇る OSL40562-IG を投入。今回は 30mA x 8 = 240mA 消費するため、三端子レギュレーター降圧では放熱上問題アリ。かといって前作のように DC/DC コンバーターを使うとスイッチングノイズで受信感度の低下が懸念される。

 そこで今回はソースドライブのトランジスタアレイ TD62783APG をハイサイドに配して 12V系から直接給電する。ただし車のバッテリ電圧は 12〜15V と変動するため、CRD で電圧変動を吸収することにした(CRD は 15mA x 2 のパラ接続で 30mA)。

 ただし OSL40562-IG のパルス駆動時の最大定格は 25℃、周期 10ms、Duty 1/10 で 100mA となっており、30mA はやや流しすぎかもしれない。劣化して輝度が低下したら素直に交換する方向で(殴)。劣化が心配ならやや輝度は落ちるが CRD 15mA + CRD 4.5mA の 20mA に留めておくべし。

 なお、CRD を使うとどうしてもコストアップになるので(15mA CRD x 16 = \480、秋月価格)、輝度変動をあまり気にしないなら普通に電流制限抵抗(安全をみて 300Ω 1/4W 程度)にする手もある。

 一方、ローサイド側は TD62083APG を介してドライブし、PIC16F886/887 の全ポート電流制限 MAX 90mA を回避する。


【その他の LED】

 LED はいつもの 2mm x 3mm の超超高輝度品を使用。PWM で輝度をコントロールするためこちらも 12V 系から給電。当初は 7 セグ LED 同様 TD62783APG 経由でドライブしてみたのだが、超超高輝度なのが災いして TD62783APG のたった 100μA のモレ電流でもソコソコ光ってしまうことが発覚(汗)。そこで別途ドライブトランジスタを用意し、ローサイド側で制御することにした。


【PWM 周り】

 スイッチング素子は効率の面から FET が望ましいのだが、今回は最大でも 240mA しか流れないので扱いやすいトランジスタにした。今回は Ic = -500mA の 2SA854S を使用したが、入手できなければ 2SA950 (Ic= -800mA) あたりがオススメ。


【消費電流】

 7 セグ LED 点灯時は最大 250mA の消費電流となるが、消灯時は 10mA 以下。この程度なら週末ドライバーでもバッテリー上がりのおそれはないだろう。


ソフトウエア:

【割込みとタイマー】

 今回は以下のタイマーと割り込みを使用している。

 (1) TMR0 割込み → TCO パルス幅計測
 (2) TMR1 割込み → コンペアモードで内部時計の秒管理
 (3) TMR2 → PWM ディマー
 (4) PORTB 状態変化割込み → TCO パルス立ち上がり、立ち下がり検出


【パルスの判断】

 最大のポイントは、受信したパルス信号をいかに 0、1、マーカー、エラー、と判断するか。このあたりは一番苦しめる楽しめるオイシイところなので、先人の知恵を借りずに自分でアルゴリズムを考えてみた(まぁ誰でも同じ方式を考えつくと思うが・・・)。

 で、通常 PIC でパルス幅を計るとなるとキャプチャ機能を使うのが最も一般的だろう。まぁ、確かにそれでも動く。規格通りの綺麗な信号だけが送られてくるならば。実際、茨城の管理人宅は室内でも受信状態は極めて良好で、写真のような非常に綺麗な信号が受信できる。実際にキャプチャでパルスを判断するプログラムを組んで確かめたところ、電波状況が良い場所ならこの方法でも時刻をデコードできることは確認できた(まぁ、当たり前のハナシだ)。

 が、しかーし。わざと受信状態を悪化させてみると、ヒゲ状のノイズが入ったり、本来 1つのはずのパルスが複数に割れたりして単純にパルス幅をキャプチャしてバッファに貯めていくだけではフレームズレの嵐となる(写真参照)。これでは車載の電波時計としては実用にならないのは明白だ(爆)。

 そこで今回は PORTB 状態変化割込みを使う。状態変化割り込みはパルスの立ち上がりと立ち下がりの両方で割り込みが発生するのがミソ。まずパルスの立ち上がりで TMR0 をリセットし、4.1ms 毎のオーバーフロー割り込みを開始。その後一定時間(以下、サンプリング時間と記す。約 900ms に設定) TCO の状態を監視し、TMR0 オーバーフロー割込み回数で何回分 TCO = H だったかを数えることでおおよそのパルス幅を判断する。もちろんこの方法では正確なパルス幅は判らないが、本来 1つのパルスが複数に分断されても対処できるというメリットがある。

 またサンプリング期間中のパルスの立ち下がり回数を数えておいて、一定以上パルスの立ち下がりがあったら「パルスが分断されすぎていて使い物にならない」と判断し、「エラー」をバッファに埋める。また想定外の長いパルスも「エラー」をバッファに埋める。この方法なら、パルスが乱れても極力フレームズレを起こすことなくバッファにデータを貯めていける。

 ただこの方法でもデータがロストした場合はフレームがズレる。この場合、受信状態が回復した後にバッファに貯め込まれた古いデータがデコードされ、思わぬ時刻ズレを起こす可能性があるので厄介。そこで TMR0 割込みの回数を監視して、パルスの立ち上がりから 1秒+α経過しても次のデータが来なければデータロストと判断して、受信バッファをクリアしてしまうことにした。もちろんデータロスト時にエラーデータを埋め続けてフレームズレを防ぐ方法もあるが、受信回復後のデコード時間を短縮するためあえてバッファをクリアしているわけだ。


【受信データのデコード】

 電波で送られてくる信号(データ)のフォーマットはこちらを参照。

 先人は受信データを 1分間分まるまるバッファに溜め込んでデコードする方法をとっている。んー、さすがは先人、実にスマート。ありがたくこの方法を使わせていただくことに。

 受信した電波には、時、分、年、元旦からの経過日数、曜日情報が含まれている。これらを全て取り出すには 1分間エラーフリーでデータを受信しなければならない。もっとも年、日、曜日にはパリティデータがないので、信頼性を確保するためには 2分間はエラーフリーで受信しなければならない。

 しかし時・分だけの復元であれば最低限必要なデータはぐっと減って、分 (2,3,4,6,7,8,9 bit目)、時 (13,14,16,17,18,19 bit目)、パリティ(37,38 bit目) の 15bit に、フレームの頭出しとフレーム同期確認用ポジションマーカー(0, 9, 19, 29, 39, 59 bit目)を加えて 21bit で済む。理論上はこれ以外の bit にはどれだけエラーデータが入っていても時刻復元は可能なわけだ。

 通常、フレームの同期確認は 60 bit 分のバッファを用意し、0,9,19,29,39,49,59 bit 目のマーカーをチェックして 1分間エラーフリーでデータが受信できたことを確認する。確かに 1分間のエラーフリーを確認するには 59 bit 目をチェックする意味はあるのだが、時刻復元だけならフレーム前半部分さえエラーフリーであればよいのでわざわざ 59 bit 目まで待つ必要は無く、-1 bit目(前のフレームの 59 bit 目)からチェックを始めれば良いことになる。

 以上を勘案し、下記手順で時刻を復元する。

 ・-1,0,9,19,29,39 bit 目がマーカーであること
 ・時、分、パリティデータが 0 か 1 であること
 ・パリティが一致すること
 ・復元された時刻が不正(68分とか31時とか)ではないこと

 以上をクリアしたら正常デコードできたとして時刻を校正する。もちろん信頼性を考えるなら 2回以上デコードして整合性を見る方が望ましいのだが、開発段階では誤デコードしたところを見たことがないので一応これでヨシとし、後は実車に搭載して信頼性を検証していくことにする。

 なおバッファサイズは 61 bit ぶん確保しているので、通常ならデータ受信開始から最低でも 61秒経たないとフレームが実時間の「正分」と合わない。が、今回は受信バッファが満タンになるまではバッファ先頭からデータを埋めていくので、理論上は最速40秒で時刻が得られる(実際、できた)。

 ちなみに受信バッファサイズは 60 bit ぶん以上が必要。それ以下だとフレーム同期のタイミングが実時間の「正分」と一致しなくなる可能性が高い。


【パリティ計算】

 公式サイトには、

-----------------------------------------------------------
 パリティビット:

 時と分に対応し、それぞれ 1ビットの偶数パリティを表します。
 PA1 = (20h+10h+8h+4h+2h+1h) mod 2
 PA2 = (40m+20m+10m+8m+4m+2m+1m) mod 2
 ( mod 2 は 2 で割った余りを示します。)
-----------------------------------------------------------

 とある。要するにデータ中「1」になっているビットが偶数個ならパリティ値 0、奇数個ならパリティ値 1、ということ。よって送られてきた時・分それぞれにパリティ値を計算し、送られてきたパリティ値と XOR をとって結果が 0 なら OK。

 もっともパリティも万全ではないわけで、例えば 7(2進 00000111)と 1(2進 00000001)はどちらも「1」の数が奇数でパリティ値が一致する。つまり受信時に複数のビットが同時に化けるとアウトで、12:01 と 12:07 はパリティが同じになる。もちろん複数ビットが同時に化ける確率はぐっと下がるので、パリティが無いよりははるかにマシではある。


【うるう秒処理】

 まったく未対処。うるう秒は取り決め上は年12回まであり得るが、これまでの運用実績はせいぜい年 1回(→ 参考サイト1参考サイト2参考サイト3)。コーディングが面倒だし、常時受信〜校正しているのでどうせすぐに校正されるとタカをくくって無視方向で(汗)


【受信周波数】

 今回は車載品なので、自動的な受信周波数変更が必要な場合もあり得る。が、下手に周波数を自動切換えにすると延々と周波数切り替えを続けた挙句、いつまでたってもデコードできない可能性が大。そこで受信周波数は手動切り替えとした(個人的には 40KHz 局圏外に車で行くことはまず無いので)。

 仕様は、

 (1) 電源投入後、EEPROM から指定周波数を読み出して受信開始
 (2) うまく受信できたらその周波数を EEPROM に保存。次回起動時に反映
 (3) 周波数はいつでも UP, DW スイッチ同時押しで強制的に変更可能。

 としたので、手動で周波数を切り替えてうまく受信できればその周波数が EEPROM に保存される。40KHz、60KHz 両方が受信できる中京〜近畿あたりでは手動切り替えは不便かもしれないが、その場合はソース中でコメントアウトしてある CALL 文を一箇所復活させるだけで周波数自動変更仕様(255 bit 受信して同期しなければ周波数を切り替えてみる)にすることもできる。


【7 セグ LED 制御】

 輝度確保のため、一般的なダイナミック点灯(桁内スタティック点灯、桁間ダイナミック点灯)とする。


【状況確認 LED】

 波形が割れていない綺麗なパルス(パルスの立下りが 1回)が受信できたら LED を点灯する。この LED が連続点灯していれば電波状況は良好と判断できる。

 また直近 4時間以内に電波校正が行われている状態なら「校正 OK」 LED を点灯する。


【ディマー機能】

 フォトトランジスタの出力を A/D 変換して読み取り、TMR2 を使った PIC のハードウエア PWM で輝度を調整する。PWM 周波数は n倍が 40KHz、60KHz にカブらないよう 12.5KHz にしたのだが実際にはあまり効果は無く(爆)、後述の通り受信感度の低下を招く要因となってしまった。

 ただ今回使用した OSL40562-IG や状況確認用 LED はあまりに高輝度で、最も PWM を絞った状態(12.5KHz DUTY 1/250)でも十分明るい。さりとて PWM 周波数は PIC クロックや DUTY 解像度との絡みであまり変更できないので、最暗状態ではハードウエア PWM に加えてソフトウエアで表示時間を短くし、輝度を落としている。(ソフトウエア PWM)


【アクセサリ電源関連】

 7 セグ LED は電気食いなのでアクセサリ電源と連動させてバッテリー上がりを防止する。エンジンスタート時に表示がバタつくと目障りなので、一定時間(1秒程度)アクセサリ電源の状態変化が継続したらはじめて表示に反映させるようにしている。

 また走行中は安定した電波受信ができないため、アクセサリ電源の状態を監視して走行中は PORTB 状態変化割り込みを停止し、デコードを行わないようにしている。走行中も受信したいなら下記スイッチ操作で強制受信も可能。


【スイッチ操作】

 ・UP スイッチ → 時刻修正:分 Up
 ・DW スイッチ → 時刻修正:分 Down
 ・SEC スイッチ → 秒合わせ
 ・UP, DW スイッチ同時押し → 受信周波数強制変更
 ・DW, SEC スイッチ同時押し → 受信開始、停止強制変更


【その他】

 7 セグ LED 表示は電波校正もしくは手動校正が行われるまで点滅表示とする(アクセサリ電源 ON 時)。


実装:

 PWM と LED ダイナミック点灯制御に由来する盛大なノイズ(爆)のため、受信部を分離(10cm以上)せざるを得なくなった。

 受信部は秋月の単四 x 2本用電池ボックスに入れ、受信部と表示部を VCC, GND, TCO, 40/60 の 4本の信号線で接続。TCO 信号は直接引き出してくるとノイズが乗る可能性が高いので、受信部内で論理反転、増幅した後に引き出す。

 表示部は HUD 速度計と同じくタカチの超耐熱性プラスチックケース SW-T75 (W50 x H30 x T75) にケーシングしたが、まだ余裕があるのでもう少し小型のケースでも収まるハズ。


感想:

 7 セグ LED に 30mA 流すことで前作の HUD 速度計同様、偏光サングラス越しでもギリギリ見えるぐらいの輝度にはなった(背景にもよるが)。でもまぁ、ぶっちゃけ言うと時計は速度計ほど頻繁に見るものではないので、無理矢理 HUD にしなくても良かったかも(核爆)

 時刻精度に関しては、停車している間に常時電波校正をしているため、時刻はほとんど狂わない。電波の強い茨城という好条件もあるが、電波時計のポテンシャルは十分に引き出せたと言えよう ^^v

 今回の醍醐味は、ノイズの混じった信号をどの程度まで許容してデータを拾い出すか、またデコードしたデータの信頼性をどの程度確保するか、といったあたりのサジ加減であった。管理人は市販の電波時計をいくつか持っており、製品によってデコード時間にだいぶ差があるのを経験上知ってはいたが、受信部の感度以外に、デコードアルゴリズムによる差も結構あるのではないか、と思う今日この頃である ^^;

 なお、世の中的にはこんなチップもあるようで、個人でも手に入ればいいのになぁ、と思ったり ^^; サンプル品くれないかしら(ぉぃ)


改良案:

 ・シールドによるノイズ対策と装置の一体化
 ・フルデータのデコード
 ・ソースを 1ヶ所書き換えるだけで通常表示(非 HUD)が可能(詳細はソース参照)
 ・シリアル経由でデータを出力できるようにする


今回の失敗:

 (1) 予想外のノイズ。PWM 由来のノイズは予想できたが、7 セグ LED のダイナミック点灯に由来するノイズはちと想定外であった。もっともトランジスタアレイを使用しなければそれほどノイズは出ないようなのだが・・・
 (2) パリティの計算方法。最初、パリティの一致判断に何を勘違いしたのか AND を使ってしまい、パリティが合わずに数日悩んだ。情報工学を学んだヒトなら「パリティ = XOR」ってのは常識なのだろうケド(汗)
 (3) 印加電圧を間違えて PIC x 2個、クリスタルオシレーター x 2個を焼き殺した(核爆・・・ 毎回同じことやってるな。学習能力まったくナシ orz)
 (4) 超高輝度 LED は TD62783APG の漏れ電流(100μA)程度でもうっすらと光ってしまった(汗)


今回勉強したこと:

 ・標準電波のフォーマットとデコード法
 ・ノイズ交じりの信号を扱う方法(ちと大袈裟)
 ・パリティの計算方法


プログラム:

 改変自由だが商用利用厳禁

プログラム v1.2 ( asm & HEX ) (2011/03/02)
HUD_Wave_Clock_v1.2.zip

謝辞:

 16bit 数値 → 5桁 BCD 変換ルーチンはこちらのライブラリを使用させていただいております。開発者に御礼申し上げます。


2011/03/13 追記:

 福島原発事故に伴なう避難指示により 40KHz 標準電波が停波中 @o@。茨城では 60KHz の受信状態が悪いので、手動で時刻合わせできるようにしておいてヨカッタ・・・ ってか、早く原発問題が片付いて 40KHz 局が再稼動して欲しいのだが・・・。


2011/04/21 追記:

 ふと見ると電波校正完了 LED が点灯していたので「あれ?」と思って調べてみたら、福島 40KHz 局が無人で運転を開始したとのこと。

 ようやく東日本の標準電波が復活したわけだが、いつまた停止するか・・・。まだまだ予断を許さない状況である。


2011/04/25 追記:

 ↑とか言ってたら落雷で機器故障 → 停波 orz ・・・・ たった4日で・・・次回復活はあるのか ?


2011/05/09 追記:

 福島局が無事復活。今後も安定して稼動して欲しい・・・


 [電子工作のページへ]