<PICで踏切音を鳴らす>⇒基本編はこちら
音源ICは意外に高価なものですので、慣れたPICマイコンを音源にすることを考えてみました。試行錯誤した結果、 そのノイハウが解ってきましたので紹介します。
PIC再生音↓



実は、最近のPICがとても高機能/高性能になってきているとはいえ、例えば音声合成(Synthesise)を しようとすると、浮動小数点演算を行う関数を作らなければならないし(PICは整数しか扱えません)、 音のサンプリングデータ(スマホのボイスメモでも16bit48KHz)を記憶させるには、大きなメモリが必要になってしまうことから、 現時点の小さなPICの能力(InternalClock:最大32MHz、ProgramMemory:最大2048Words)に実装するには制約があるのです。

↓JR踏切音の録音波形(16bit48KHz/450mSec)


まずは、プログラムを披露します(音データは含まず)。main()のデータ再生部はとても簡素にすることができましたので、 interrupt isr()には2組の警報灯(LED交互点滅)とミュートモードへの切り替えを追加しています。init()はI/Oの設定の他は、PWMとTimer/Interruptの設定です。

当初の想いは、
・繰り返し再生だけできればよい
・簡単な回路にしたい
・実際の音に近づけたい
・踏切音以外にも使いたい
・8pinDIPのPICで小型化したい

というコンセプトで始めたその結果のエッセンスは、
・リアル音のサンプリングデータ:6bit8kHz(450mSec)
・データを焼くエリア:ProgramFlashMemory (1792Words)
・再生周波数:15.625KHz(Clock:16MHz)
・スピーカー直結可:10bitPWM出力によるD級ドライブ
という結論を得て、使用するPICは12F1572に落ち着きました。
↓回路図


なおProgramエリアにデータを焼かず(SRAM/EEP-ROM)に済ませるための簡便な方法として、基音(2音だけ)を合成する簡易シンセサイザーでもそれなりの音は出すことができます。
・JR踏切音の基音:700Hz/750Hz
・小田急電鉄の基音:450Hz/550Hz
リアル感は落ちますが、PICで音を創る基本を理解するためには参考になると思いますので、作り方を次ページに紹介いたします。
↓JR踏切音の周波数スペクトラム・・・・・↓小田急電鉄踏切音の周波数スペクトラム

<メモリ>
初めにメモリを考えてみます。記憶させる音の長さとサンプリング周波数でデータの大きさは決まります。
・踏切音ベル1回(カン)の長さ:450mSec
・各種踏切音の帯域:300Hz~3.5KHz
をサンプリング周波数48KHzでつくると0.45/(1/48000)=21600個です。PICのMemory1wordは14bitですので、 振幅を16bitとせずに14bit階調の音としても21600Wordsが必要となり制約をはるかに超えます。2048Wordsに入れるには 2048/0.45=4551Hz以下の4KHzでサンプリングすればよいわけですが、4KHzの再生周波数はサンプリング定理fs/2から2KHz以上の音を再現できないだけでなく、 可聴帯域ですのでフィルタがないと音として聴こえてしまいます。 そこでMemory1Wordを2byte(上位7bitと下位7bit)に分け4096byte(7bit/Byte)使えるようにすると、 4096/0.45=9102Hz以下の8KHzサンプリングが可能になり、再生帯域をカバーできます。踏切音データは0.45/(1/8000)=3600Byte=1800Wordsです。 これで残りの2048-1800=248Wordsをプログラムに使うことができるのですが、8KHzもまだ可聴帯域内にあります。 従って再生周波数(PWM)を可聴帯域外の16KHzに上げて、データを2回づつ使うという技でサンプリング8KHzを再生しました。

<クロック>
次に再現できる振幅階調を考えてみます。PWMはパルス幅により振幅階調を表現しますので、クロック周波数とPWM周波数から生じる制約があります。 仮に16bit階調を得ようとしたとき、32MHzクロックにおいては1/32MHzでパルス幅が刻まれるため2^16*(1/32000000)=2.048mSec のPWM周期つまり488Hzとなってしまい、踏切音の帯域を再生できません。 同様に10bitPWM機能を持つPICの32MHzクロックでは、1/(2^10*(1/32000000))=31.25KHzのPWM周波数で再生することができます。 32MHzクロック時に10bit32KHzですから、16MHzで10bit16KHzとなり、7bit16KHzは逆算して2^7*16000=2MHzのクロックで作れることになります。 しかし実は、PWM周期がクロックを階調bit分のカウントして作られるため、階調bitを減らすとPWM周期中のクロック数が減ることになり、 一周期中のクロック数では振幅値を更新するための処理が終わらないという状況が発生してしまいます。7bit16kHzの場合2^7=128の2周期分256クロックが使えますが、 実験的には最低でも256(8bit)*2のクロック数を確保する必要があることが判りました。従ってこの制約から、8bit16KHzのPWMを用意するために2^8*16000=4MHz以上のクロックが必要となります。

<実装のポイント>
さて実際のプログラミングを考えると、14bit/Word上位6bitと下位8bitには個別にアクセスできるため、7bit/byteで使うよりも下位byteの2bitを使わずに6bit/byteにした方が、 効率的にデータを作れて簡単に扱うことができます。そして(10bitPWMの場合)PWMパルス幅は、上位8bitと下位2bitを個別に設定するアーキテクチャであることから、下位2bitを使わずに6bitデータを上位byteにそのまま 代入できると煩わしさがなくなります(16bitPWMでは上位8bit/下位8bitなので、気にする必要なし)。従って上位6bit+下位2bit(00)=8bit以上の振幅階調でPWMを使えばコードが簡単になり、かつクロック数の条件も満たすことになります。
記憶領域の都合からくる6bit8kHzデータをシンプルに再生するために、4MHzクロックの8bit16KHzPWMを使えば十分なことは判りましたが、Synthesizerへの応用も考慮に入れてPWMは10bitフルに使い クロック周波数は16MHzを採用しておくことにしました。こうすることで6bitの振幅データは、最大値の1/16(-4bit)となるため16倍の振幅変化を付けることが出来るようになります。 下位2bit(00)を付け加えた8bit振幅としても、4倍(2bit)の振幅変化を与えられます。この余裕はミュート機能の盛り込みに使用しました。 なおPWM周波数の16KHzを15.625KHzとしたのは分周(2進数)の扱い易さによるもので、プログラム中ではPWM3PRL=250とすれば正しい音程(16KHzPWM)での再生となりますが、振幅階調の余裕は1bit減ります。

戻る