たまりば

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

最弱のPICマイコンで電子オルガン_前編
2013年03月26日 01:34

最弱のPICマイコン、PIC10F200で電子オルガンを作ってみた。

【ニコニコ動画】最弱のPICマイコンで電子オルガンを作ってみた

'13.5/5 後編できました。

電子オルガンはマイコン工作の定番である。
必要なのはキー入力がいくつかと音の出力が1つ。キー数は今回1オクターブ白鍵のみの8鍵とした。
8個のキーを読もうとすると、普通に考えれば8本の入力が要る。
単純な多キー入力
マトリックスを組めば、3×3=9なので6本で済む。
キーマトリックス
だが、10F200のI/Oピンは4本しかないので、これでも足りない。
ADコンバータを使って1ピンで多数のキーを読む手法もあるが、
ADコンバータによる多キー入力
10F200にはADコンバータは無い。コンパレータすら無い。
ではどうするかというのが今回のメインテーマである。
(実際にはキー入力をいくつ取れるか考えたのが先で、使い道として電子オルガンを思いついた)

解説を書いていたのだが、どうも長くなりすぎて頭がまとまらなくなってきたので、前後編に分けることにする。
前編ではメインのキー入力の扱いについて説明し、残りのこまごまとしたものは全て後編に回す。
なお通常のキーマトリックスの扱いは理解している前提で進める。

まず参考になるのがCharlieplexingだ。
これはCharlieさんが考案した、少ないピンで多数のLEDを駆動する結線である。
Charlieplexing LED
このように結線することで、N本のピンでN×(N-1)個のLEDを個別に制御することができる。

これをスイッチに応用する。
Charlieplexing スイッチ(誤)
こうはならない。
ダイオードを入れない限りスイッチは双方向なので、半分しか使えない。
Charlieplexing スイッチ(半分)
こうだ。
しかしスイッチならではの利点もあって、スイッチはLEDと違って受動的なのでGNDとの導通も見ることができる。
つまり、LED出力の場合はGNDとの間に電圧を掛ければ常に点いてしまうが、スイッチ入力は電圧を掛けたうえでスイッチが押されなければ何も入力されないので、GNDも有効に使えるというわけだ。
Charlieplexing スイッチ GND使用
結果、こうなる。これで4ピンで10キーの入力が取れる。

これに加えて圧電スピーカーへの出力が必要だ。
スピーカー出力とキー入力の両立については詳しくは後編で書くが、結論だけ言うと扱えるキーが1個少なくなるだけで両立可能である。

なお、GNDを使っているため走査が特殊になる。

普通に走査するなら、1度に1つのピンのみを出力とし、残りをプルアップで入力にしておくのが分かりやすい。
時間→
ピン0
ピン1
ピン2
ピン3
ピン4

しかし今回の配線では、GNDは常にLを出力していることになるので、このような走査をすることにした。
時間→
GND
ピン0
ピン1
ピン2
ピン3

読んだ値を解釈するコードが場合によっては多少複雑になるかもしれないが、今回はテーブルを引いて変換したため問題にならなかった。
テーブルについてもちょっと面倒なことがあったが、それは後編に回す。

全プログラムは下に載せたが、概要を示すとこんな感じ。

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

他に無音時の処理があるが後編に回す。

なお、ビジーウェイトの常として、プログラムの命令数を正確に数えておく必要があり、しかもプログラムを変更したら数え直しである。その上、数サイクル数え間違ったところで、微妙に音がずれるだけなので自分の耳では判別不能である。
何が言いたいかというと、下に載せたコードは数え間違いがある可能性が高い。でもめんどいので気にしない。

ところでこのビジーループの部分のコードであるが、実はこれを書いた後にもっといいコードを思いついた。そのうち解説したい。

    list    p=10F200
    #include p10f200.inc

    ;    N/C+-v-+(GP3)
    ;    Vdd|   |Vss
    ;    GP2|   |N/C
    ;CLK GP1+---+GP0 DAT

    __CONFIG _MCLRE_OFF & _CP_OFF & _WDT_ON
    ;MCLRE : GPIO3を使いたいのでOFF
    ;コードプロテクト : Don'tCare
    ;WDT : 消費電力低減のためSLEEPするので、起こすために必要

    cblock    0x10
        waitcount ;ウェイト用カウンタ
        keytemp ;押されたキーの判定に使う一時変数
        period ;音の周期の一時変数
    endc

#define keyReadTime d'20' ; 発音サイクルのキー読み側の時間
#define nonKeyReadTime d'9' ; 発音サイクルの非キー読み側の時間
#define waitTimeDiff d'10' ; waitxで指定するカウント数と実際のwait時間の差
#define dif (waitTimeDiff+keyReadTime) ; 音程に指定する値と実際の音程に相当する値の差

;押されたキーの組み合わせと音高(半周期のcycle数、1で無音)の対応

;例
;k03 :   GPIO0-GPIO3間のキー
;kg1g3 : GND-GPIO1間のキーとGND-GPIO3間のキーの同時押し

k0 equ d'1' ;押されていないことを表す。decfszで見るため0でなく1。

k03 equ d'239' - dif ;c
kg1 equ d'213' - dif ;d
kg0 equ d'190' - dif ;e
kg3 equ d'179' - dif ;f
k01 equ d'159' - dif ;g
k23 equ d'142' - dif ;a
k02 equ d'127' - dif ;h
k12 equ d'119' - dif ;C

kg103 equ d'225' - dif ;cd同時押し、c#
kg0g1 equ d'201' - dif ;de
kg0g3 equ d'201' - dif ;ef 使用しないが、k0223を反応させるために必要
kg301 equ d'169' - dif ;fg
k0123 equ d'150' - dif ;ga
k0223 equ d'134' - dif ;ah
k0212 equ d'1' ;hC
k0312 equ d'1' ;Cc

kg1g3 equ d'1';
k0103 equ d'1';
k1223 equ d'1';
kg012 equ d'1';
kg023 equ d'1';
kg102 equ d'1';
kg123 equ d'1';
kg302 equ d'1';
kg312 equ d'1';


init
    org 0x00
    movwf OSCCAL ;補正
    ;補正しないと音が狂う
    ;もっとも、補正しても±1%=17セントと大分狂っている
    
    ;スリープからの復帰の場合は不要な初期化処理を飛ばそうとしたが、
    ;いまいち仕様がよく分からなかったため、やめた
;    btfss STATUS, NOT_PD ; /PD=0 then wake up from sleep
;    goto afterSleep

    movlw b'10000000'
    ;       ^/GPWU=1 : ピン変化によるウェイクアップは使用しないためOFF
    ;                  WDTでなくこちらを使うことも考えたが、全キーに反応させるのは多分無理。
    ;        ^/GPPU=0 : PullUp ON
    ;         ^T0CS : タイマは使用しないためDon'tCare
    ;          ^T0SE : タイマは使用しないためDon'tCare
    ;           ^PSA=0 : WDTを最速で動かすためプリスケーラをTMR0に割り当て
    ;            ^^^PS2:0 : タイマは使用しないためDon'tCare
    option

main
    movlw b'00001011'
    tris GPIO

mainloop
    call readkey;<-18->
    movwf period
    movwf waitcount ;20->
    call waitx
    decfsz period,W ;無音を分岐; if period-1=0 then sleep
    goto soundOn
    sleep
soundOn
    clrwdt
    bsf GPIO,2 ;圧電スピーカーへの出力をH ;22+waitx ->|
    ;<-9+waitx
    movlw nonKeyReadTime ;キー読み分の時間調整
    addwf period,W
    movwf waitcount
    call waitx
    goto mainloop

readkey
    ;8つのキーの組み合わせを読む
    ;不自然にビット反転しているのはテーブルに隙間を都合よく開けるため
    
    swapf GPIO,w ;発音時であれば、p2がHの状態でGNDのみとの導通を見る x?xx0000
    ;待機時であれば、次の入力取得と同じものが取れるため、p2-XのキーとGND-Xのキーを誤認するが、
    ;次回からは発音しているので正しいキーが取れるため問題ない。
    ;keytemp[6]は、発音時は1、無音時は0になる
    bcf GPIO,2 ;圧電スピーカーへの出力をL / 9+waitx->|
    ;|<-
    movwf keytemp
    comf GPIO,w ;p2またはGNDとの導通を見る 1111y0yy
    andlw b'01001011' ;p0,1,3相当のデータを残す。6bit目は1に 0100y0yy
    iorwf keytemp,f ;上記2つのデータをまとめる x1xxy0yy
    bcf GPIO,0 ;p0をL
    movlw 0x0A
    tris GPIO
    btfsc GPIO,1 ;p0p1間の導通を見る keytemp[2] = P1
    bsf keytemp,2
    btfsc GPIO,3 ;p0p3間の導通を見る keytemp[6] = /P3
    bcf keytemp,6
    movlw 0x0B
    tris GPIO
    movf keytemp,w
    movwf PCL ;16 + retlw = 18cycle (call含まず)

waitx ;waitcountの値に応じてウェイト。waitcount >= 4
;call・return含め、(waitcount -4 +14)cycle待つ。
    comf waitcount,W ;下2bit相当の待ち→
    andlw 0x03
    addwf PCL,f
    nop
    nop
    nop ;→|

    rrf waitcount,w ;上位6bitの分だけ4サイクルのループ→
    andlw 0x7E
    movwf waitcount
    decf waitcount,f
    decfsz waitcount,f
    goto $-2 ;→|
    retlw 0


;キー押下パターンテーブル
;
;55 ← テーブル中の隙間の数(めぼしいもの)
    org d'57'
    retlw kg302
;32
    org d'90'
    retlw kg1g3
;18
    org d'109'
    retlw kg0g3
;10?
    org d'120'
    retlw kg301
    org d'124'
    retlw kg3
    org d'126'
    retlw kg312
    org d'131'
    retlw kg0g1
;14
    org d'146'
    retlw kg1
    org d'147'
    retlw kg102
;15
    org d'163'
    retlw kg012
    org d'165'
    retlw kg0
;10
    org d'176'
    retlw k01
    org d'178'
    retlw k12
    org d'179'
    retlw k0212
    org d'180'
    retlw k0
    org d'181'
    retlw k02
;28
    org d'210'
    retlw kg103
    org d'218'
    retlw kg123
;18
    org d'237'
    retlw kg023
    org d'240'
    retlw k0103
    org d'242'
    retlw k0312
    org d'244'
    retlw k03
    org d'248'
    retlw k0123
    org d'250'
    retlw k1223
    org d'252'
    retlw k23
    org d'253'
    retlw k0223

    end

タグ :マイコン

  • Post time : 2013年03月26日 01:34│Comments(0)
    URL欄を実験的に消してる間に廃止されてしまいました。まあいいか。
     
    <ご注意>
    書き込まれた内容は公開され、ブログの持ち主だけが削除できます。
    削除
    最弱のPICマイコンで電子オルガン_前編
      コメント(0)