たまりば

パソコン・インターネット パソコン・インターネット三鷹市 三鷹市

最弱のPICマイコンで電子オルガン_後編
2013年05月05日 02:58

前編から読んで下さい。

前編では今回のメインのアイディアである4ピンで多入力を扱う方法について説明した。
後編ではスピーカー出力と、その他の細かいことを説明する。

スピーカー出力との両立

前編で説明したのは入力のみである。
前回の図
これに加えて圧電スピーカーへの出力が必要だ。
スピーカーに繋いだピンはキー走査のために使うのは無理かと最初は思っていたが、これだと6キーしか取れず1オクターブに足りないので、頑張って考えたところなんとかなることが分かった。
キーの走査の際には「プルアップされた入力ピン」と「スキャンのためにLowを出力するピン」の間が導通するかどうかを見る。
スピーカーへの出力はLowとHighを交互に出力している。これに加えて入力にもするのは不可能か、そうでなくてもタイミングがシビアだろう。また残念ながらPIC10F200には1本プルアップ抵抗の使えないピンがあるため結局不可能である。
なので入力側として使うのはやめ、出力側としてだけ使うことにする。
通常の出力側はLowとHigh-Zを出力するところ、これはLowとHighを出力している違いはあるが、プルアップしているピンにHighが入力されても問題ない。(ただし同時押しをした場合は他のLow出力と衝突するパターンがあるので注意が必要である)
というわけで、入力側として使われることの少ない、一番下のピンをスピーカーへの出力として使うことにする。
9キー入力+圧電スピーカー出力
これで1キー使えなくなり、都合最大9キーとなる。
PIC10F200のピンとの対応は、入力専用ピンのGP3とプルアップ抵抗の使えないGP2を適に配置して次のようになる。
回路とPIC10F200のピンの対応
(動画に入れた図の使いまわしのため描き方が変わったが気にしてはいけない)

なお実は頑張って考える前には6キーバージョンも作っている。6キーに沖縄音階のドミファソシドを割り付けて、同時押しでレとラが出る。
6キー版

テーブル引き

前編で説明したように、走査した入力パターンから押されたキーを判定するにはテーブルを引いている。
そのテーブルを引くコードだが、1つ問題がある。
まず、読む情報は、
a. GP2をHighにした状態で、GP0,1,3への入力
b. GP2をLowにした状態で、GP0,1,3への入力
c. GP2およびGP0をLowにした状態で、GP1,3への入力
d. GP2,0,1をLowにした状態で、GP3への入力
の計9bitになる。
切りが悪いので、dの入力は使わないことにして、8bitで8キーを扱うことにする。
8bitのテーブルは256エントリ必要だが、10F200のFlashメモリも256ワード。
つまりテーブルだけでFlashが埋まってしまう。
…わけだが、実際のところキー入力の状態はそう多くない。9キー分に同時押しを加えてもテーブルはスカスカである。
音高テーブルとコード領域
赤で示した部分がキー入力データの存在する場所で、青の部分はテーブルとして参照されることが無い(と想定している)場所である。プログラムは青の部分に入れることにした。(と言っても最初の隙間に全て収まっているが)
ただ、後で気づいたがこれは2キー同時押しまでしか考慮していないパターンで、3キー以上の同時押しをすると想定外の場所が参照されてしまう。まあそもそも想定外の同時押しはショートする場合があるので、元から運用でカバーする設計ではある。

無音時の処理

前編で示したプログラムの流れは以下のとおりである。

キーを読む
 この際、スピーカーの状態がHighからLowになる
読んだ値を1バイトにまとめて音高のテーブルを引く
音高に応じてビジーウェイト
スピーカーをHighに
音高に応じてビジーウェイト
最初に戻る

これだけでも一応動作するが、1つ問題がある。
上で書いたようにスピーカー出力とキー走査を1つのピンで行っているため、キーを正しく走査するためにはこのピンからHighとLowの両方を出力する必要がある。これはすなわち音が鳴るということで、なるべく音を小さくすることはできるが完全に消すことはできなかった。
どうしたものかと悩んでいたのだが、結論として、キーを「正しく」走査する必要は無かった。
スピーカーへのピンをHighにして取るべきデータをLowで取った場合、押されたキーを誤認するものの、キーが押されたのを見逃すことはない。
その結果、別の音を鳴らしてしまうのだが、1周期後には発音時の走査で正しい値が取れる。1周期分の音(そもそも音と呼べるのかすら怪しい)は人間には判別できないため、全く問題なく動作していることになる。

また、無音時にも発音時とそう変わらない電力を消費してしまい、無駄であったので、無音時にはスリープを使って省電力化を図った。

発音時と無音時に分けたプログラムの動作は以下のとおり。

[発音時の動作]
キーを読む (圧電スピーカーはHighの状態)
 読む最中に圧電スピーカーをLowにする
キーに応じて音高のテーブルを引く
引いた結果が「無音」なら、※へ
音高に応じてビジーウェイト
圧電スピーカーをHighに
音高に応じてビジーウェイト
最初に戻る

[無音時の動作]
※ スリープする
WDTで約18ms後に起きる
(ここからは発音時のコードの最初に入る)
キーを読む (圧電スピーカーはLowの状態)
 読む最中に圧電スピーカーをLowにする(つまり変化なし)
キーに応じて音高のテーブルを引く
引いた結果が「無音」なら、※へ
音高に応じてビジーウェイト
圧電スピーカーをHighに
音高に応じてビジーウェイト
最初に戻る

スリープの効果はというと、
発音時の消費電流は音高によって異なるが200μA~300μA程度。
スリープしていなかった時の無音時は200μA程度。
これがスリープにより無音時1.6μAほどまで激減している。これはデータシートにあるウォッチドッグタイマの消費電流と同レベルであり、限界と言える。
1.6μAがどの程度の電流かというと、よくあるボタン電池のCR2032は容量が大体220mAh程度ある。
220mAh / 1.6μA = 15.7年
であり、無視できるレベルである。

基板

配置は、頑張れば斜めに半分に切った基板に収まりそうだったので、頑張って収めた。
本体と基板の余り
その裏
もっとも、残り半分を使うあてはない。
配線図がこちら。
配線図
部品の下にジャンパ線って萌えるよね。
  
タグ :マイコン