開発経緯・設計コンセプト
年末に職場の就業時刻のチャイムが鳴る掛け時計が壊れました。
実は他の部屋にも異常に時刻が進む時計や、故障して頻繁にチャイムが鳴らない時計があったりと散々な状態でした。
年末の忙しさもあってか誰も新しい物を購入しようと言い出さなかったため、正月休みの自由研究というノリでチャイムが鳴る時計を作る事にしました。
要求仕様は、休み時間を含めて時刻を最低でも10個登録でき、チャイムが鳴る、時刻が狂わない時計です。
設計コンセプトとして、会社で使用するものですので自費で部品を購入せず、可能な限り低予算で作るという縛りルールを設けます。
幸か不幸か私が働く職場は電子部品を扱っており、死蔵在庫になったLEDやマイコンが転がっていたので、これを利用させていただきました。
要求仕様は、休み時間を含めて時刻を最低でも10個登録でき、チャイムが鳴る、時刻が狂わない時計です。
設計コンセプトとして、会社で使用するものですので自費で部品を購入せず、可能な限り低予算で作るという縛りルールを設けます。
幸か不幸か私が働く職場は電子部品を扱っており、死蔵在庫になったLEDやマイコンが転がっていたので、これを利用させていただきました。
ハードウエア
回路図
ファームウエア
基板はハギレで残っていたユニバーサル基板を使い、サイズはタカチのYM-130にちょうど収まるサイズに仕上げました。 ケースを購入できないためハギレのプラパンを上下に取り付けて基板を保護しています。
初期の検討段階でチャイム音は適当なメロディICを使って音を鳴らそうと考えていたのですが、ウェストミンスターの鐘(いわゆる学校チャイム)のメロディICは入手性がかなり悪く、それなりに高価だったのです。 PICでビープ音を鳴らしてみようかとも考えましたが、あまりにもチープすぎる音でボツ。 たまたま手元の部品箱を見ると32kバイトのEEPROMが数個あったので、これにPCMデータを書いて読み出せば万事解決ではないかと思い、メロディの再生もPICに任せることにしました。
EEPROMから読み出したデータは4bitのR-2RラダーDACを通してアナログ値に変換し、パワーアンプIC LM386を使ってジャンク箱にあったスピーカを鳴らします。 通常はアンチエイリアスノイズを除去するためにローパスフィルタを噛ませることになるのですが、基板サイズや費用の観点から大幅に省略することになってしまい、LM386で利用できるバスブースト回路を使ってノイズカットを簡易的に実装しています。
ファームウエア
基板はハギレで残っていたユニバーサル基板を使い、サイズはタカチのYM-130にちょうど収まるサイズに仕上げました。 ケースを購入できないためハギレのプラパンを上下に取り付けて基板を保護しています。
初期の検討段階でチャイム音は適当なメロディICを使って音を鳴らそうと考えていたのですが、ウェストミンスターの鐘(いわゆる学校チャイム)のメロディICは入手性がかなり悪く、それなりに高価だったのです。 PICでビープ音を鳴らしてみようかとも考えましたが、あまりにもチープすぎる音でボツ。 たまたま手元の部品箱を見ると32kバイトのEEPROMが数個あったので、これにPCMデータを書いて読み出せば万事解決ではないかと思い、メロディの再生もPICに任せることにしました。
EEPROMから読み出したデータは4bitのR-2RラダーDACを通してアナログ値に変換し、パワーアンプIC LM386を使ってジャンク箱にあったスピーカを鳴らします。 通常はアンチエイリアスノイズを除去するためにローパスフィルタを噛ませることになるのですが、基板サイズや費用の観点から大幅に省略することになってしまい、LM386で利用できるバスブースト回路を使ってノイズカットを簡易的に実装しています。
メロディの再生処理について
一般的な学校チャイムのメロディの長さはおおよそ20秒程度あります。これを8ビットのベタPCMで32kバイトのメモリに書き込む場合、1秒当たりのサンプリングレートは1,638Hzにしなければいけません。
サンプリングレートが1,638Hzなら記録できる音声帯域は819Hzしか確保できず、案の定こもって汚い音しか出ませんでした。
そこで、メロディはチャイムの前半のみとし、速度を速めて再生時間は最長8.192秒に限定、さらに量子化ビット数を4ビットに減らしてトータル65,536サンプルを記録することとしました。 この記録方式から逆算したサンプリング周波数は8kHzで、再生可能な理論上の音声帯域は4kHzと大幅に向上しました。
再生に必要なタイミングはPIC内蔵のタイマー割り込みで125usを発生させ、EEPROMから9ビット分の読み出しに必要な時間23.4usはMSSPモジュールに任せて、DACへの出力とデータの読み出しを並行して行っています。 計算上ではサンプリング周波数を22kHz程度まで速くすることができ、実際にやってみると1秒程度しか再生できませんが音質を劇的に向上させることもできます。
最終的に余っていた基板の面積にEEPROMを3個搭載し、8秒のメロディを2個、4秒のメロディを2個、計4種類のメロディを選択できるようにしました。
そこで、メロディはチャイムの前半のみとし、速度を速めて再生時間は最長8.192秒に限定、さらに量子化ビット数を4ビットに減らしてトータル65,536サンプルを記録することとしました。 この記録方式から逆算したサンプリング周波数は8kHzで、再生可能な理論上の音声帯域は4kHzと大幅に向上しました。
再生に必要なタイミングはPIC内蔵のタイマー割り込みで125usを発生させ、EEPROMから9ビット分の読み出しに必要な時間23.4usはMSSPモジュールに任せて、DACへの出力とデータの読み出しを並行して行っています。 計算上ではサンプリング周波数を22kHz程度まで速くすることができ、実際にやってみると1秒程度しか再生できませんが音質を劇的に向上させることもできます。
最終的に余っていた基板の面積にEEPROMを3個搭載し、8秒のメロディを2個、4秒のメロディを2個、計4種類のメロディを選択できるようにしました。
計時処理について
時計のための時間計測処理は、16bitタイマーのTimer1を使って割り込みでカウントしています。
クロックに20MHzを使っているので、1/4のスピードで動くPICのタイマーは1カウントが0.2us(5MHz)、これを12,500回カウントして2.5ms(400Hz)の基準信号を作ります。 さらにこれを200回カウントして秒表示のドットを0.5秒周期で点滅させ、あとは60秒、60分、24時間のカウントすれば時計の出来上がりですね。
ちなみに2.5msの基準信号は7セグLEDのダイナミック点灯の周期の元にもなっており、4ケタで10ms、つまり1秒間に全桁を100回書き換えていることになります。
60秒がロールオーバーしたタイミングでアラームの登録時刻をチェック、アラームは84個登録できるようになっています。 登録数が中途半端なのは、256バイトの内蔵EEPROMに時・分・アラーム種別の3バイトを入れているためです。
クロックに20MHzを使っているので、1/4のスピードで動くPICのタイマーは1カウントが0.2us(5MHz)、これを12,500回カウントして2.5ms(400Hz)の基準信号を作ります。 さらにこれを200回カウントして秒表示のドットを0.5秒周期で点滅させ、あとは60秒、60分、24時間のカウントすれば時計の出来上がりですね。
ちなみに2.5msの基準信号は7セグLEDのダイナミック点灯の周期の元にもなっており、4ケタで10ms、つまり1秒間に全桁を100回書き換えていることになります。
60秒がロールオーバーしたタイミングでアラームの登録時刻をチェック、アラームは84個登録できるようになっています。 登録数が中途半端なのは、256バイトの内蔵EEPROMに時・分・アラーム種別の3バイトを入れているためです。
時刻ずれの補正機能について
数年前に作った時計で気になっていた時刻のずれ、今回の作品でも組み立て当初は1日12秒くらい時刻を速くカウントしていました。
数値で表すと140ppm程度速い事になり、目で見ても明らかに秒ランプの点滅が速いのです。
想像以上の誤差でソフトのバグを疑いましたがシミュレータでは全く狂わなかったので、半信半疑で別の水晶に交換してみると今度は大幅に遅く、1日20秒(-260ppm)くらい遅れました。
使っている水晶が古すぎてボケているのか、使ったユニバーサル基板が吸湿していたのが悪いのか、そもそもユニバーサル基板で精度を求めるのが悪いのか、個人の工作レベルでは調査するすべが見当たらず、ソフトで対応することにしました。
先の計時処理の解説で12,500回カウントと書いていましたが、これを1減らして12,499回に変更すると80ppmの変化量となります。1日当たりに換算すると6.9秒も動くことになるので調整幅としては粗すぎます。 そこで12,500回のカウントを256回分カウントし、そのうちのN回を12,499回に変更すると、80ppm/256=0.3125ppmの精度で微調整することができます。
コードサイズもさほど増えないため早速実装し動作テストを行ったところ期待していた通りの動きとなり、3日ほど経過しても目につく誤差はすっかり解消しました。 実機の動作はリンクしている動画でも解説しているのでぜひご覧ください。
先の計時処理の解説で12,500回カウントと書いていましたが、これを1減らして12,499回に変更すると80ppmの変化量となります。1日当たりに換算すると6.9秒も動くことになるので調整幅としては粗すぎます。 そこで12,500回のカウントを256回分カウントし、そのうちのN回を12,499回に変更すると、80ppm/256=0.3125ppmの精度で微調整することができます。
コードサイズもさほど増えないため早速実装し動作テストを行ったところ期待していた通りの動きとなり、3日ほど経過しても目につく誤差はすっかり解消しました。 実機の動作はリンクしている動画でも解説しているのでぜひご覧ください。
製作過程で苦労した点・使ってみた感想
- ブレッドボードを使ってテストしていると、LEDのダイナミック点灯のスイッチング音がアンプを通って非常に大きなノイズになって表れたこと。 GNDを1点アースとして集中させるとノイズが無くなった。眉唾だと思っていたがこんなに効果があるものだと実感して大変驚いた。 ユニバーサル基板に実装するときもアナログ系とデジタル系を1点でGNDに接続している。
- PCM再生の音質が異常に悪く、例えて言うならギンギンした音になってしばらく原因がわからなかったこと。 DACの後にアンチエイリアスフィルタを付けなければいけないのをすっかり忘れていた。
- 時刻の誤差の原因を究明したところ。水晶を使っているのにいくらなんでも誤差多すぎ。TCXOやRTCモジュールなら補正機能が付いてて楽なんだろうなぁ。
- 機能・精度共に満足のいくものができ、職場でもおおむね好評だった。