オルゴールに必要なサブルーチン群
音楽データ1バイト(オクターブ:トーン)を、サイン波上の角速度に変換するルーチン。
PIC12F1822には便利な FSR(File Select Register) が2つあり、間接アドレシングが可能。
41、42行目で、FSR0が0x40と設定されてコールされると、FSR0がTone0、FSR1がTone0+1をアドレスするようになる。
50、53行目のように、MOVWF INDF0 で WREG(ワークレジスタ)の値が間接アドレスされて Tone0 に格納される。
FSR0 と INDF0、FSR1 と INDF1 が対応してC言語でいうところのポインタとその中身の関係。
49、52行目の CALL 文、WREGにINDEX値を設定してテーブル名をCALLすると、INDEX値に応じた結果をWREGに入れて返してくれる。
テーブルの書き方は、以下のように、BRW(Relative Branch with W)を先頭に、中身を RETLW(Return with Literal in W) で列挙する。
1778行 D’0’などの D は10進数(Decimal)表記の意。
↑プログラムの最後にテーブルを置いた。1805行目のプログラムアドレスは0x7FF。
プログラムの最後には END を書く。
71、75行目のように、FSRはMOVWI、MOVIWで使うとき、相対指定も可能。最初に設定された FSR0 が0x40(=Tone0)だった場合、2[FSR0] は Lngt0 、3[FSR0] は Lngt0+1 を指すことになる。
70、74行目の CALL 文もデータに応じた値をテーブルから取ってきている。テーブルは以下。
PWM値を設定したあと角速度分つぎのサイン波リード位置をひとつ進めるため累算するルーチン。
82、84行目のように MOVIW 命令では、FSR相対指定による間接アドレス読み込みができる。3C[FSR0]の3Cは、意味的には-4。
相対指定は、-32~31の範囲で可能だが、コードで書くときは16進補数表現にしなければならない。
85、87行目は、FSR値をリテラルで加減算できる。この場合1を足すだけなので、INCF FSR0,F と書くことも可能。93行目は-1の例。
94行目、MOVIWはまた FSR0-- のように間接アドレス読み込みとポストデクリメントのようなことができて便利。
95~97行目は、累算結果を整数でまるめる(意味は四捨五入、正確には127捨128入)処理。サイン波テーブルをより正確に引く。
エンベロープカウンタ(ポインタ)の更新とその値(位相差)を設定するルーチン
104~106行目、こちらの方「ELM - WaveTable電子オルゴール」のアタック期間テーブルを使わせていただきました。アタック期間とサステイン期間を合体させるとテーブルが4ページ(256バイトx4)に及びます。そのアタック期間はサイン波を減衰させない処理。
107~110行目、エンベロープテーブルは0x80バイト(128バイト)なのでカウンタが128を超えないようにする処理。
111~113行目、EnvC[0..2]から値を読み込み、エンベロープテーブルで取得した値を、EnvV[0..2]に設定。
以下エンベロープテーブルは長いので途中割愛してあるが、データ数128個、上限値を64にした右肩あがりのグラフにすればよい。
以下参考:グラフ化したもの
データ構造1チャネル分をクリアするルーチン
今回のプログラムではチャネルの使用状況を LpBit という変数の中の3bitで管理する。1チャネル使う前に以前のデータを消去するのに使う。
1チャネルを確保し、中身を消去するルーチン
累算し四捨五入した値を使ってPWMに設定する値をテーブルから取得するルーチン
166~172行目、4ページにまたがるテーブルからインデクス値に応じた値を取得するため、PCL(プログラムカウンタ)の加算を利用しています。他にも実装方法があるかも知れませんが割りと短いコードで済んでいます。
193行目からが、サイン波の重ね合わせによる減衰を実現しているところです。サイン波の値は0~255までの範囲なので、それぞれ2で割ってから足し算しています。
以下テーブル。長いので途中割愛していますが、先ほどのリンク先からデータは入手できます。サステイン部分は自分でサイン波を128個作り足します。
1470行目、Sustainラベルの下に BRW が無いことに注意。att4 テーブルは Sustain まで含めて256バイト。
各チャネル出力値を合算するルーチン
単音から3和音まで、少しずつ出力が大きくなるようになっている。
200ms待ちルーチン
曲間の待ち時間を作ったり、外部EEPROMの起動を待ったり、ボタンのチャタリングを待ったり、といろいろ使える。
曲データの読み込みルーチン
外部EEPROMからの読み込みに改造する場合は、この部分を置き換えてデータをバッファにためるようにする。
今回は簡易的にデータテーブル以下で実現。
0xFFを2つ書くとメインループの中で曲データ最後を認識できる。
ここまで細かなサブルーチン群と広大なデータテーブルでした。
PIC12F1822はプログラム領域が2048バイトしかないので、テーブルだけで1300バイト超えている本プログラムは切り詰めが必須です。
次回はメインループ予定。