MIDIコントローラ

  1. MIDIコントローラ
     
    1. MIDIとは
       MIDIは音楽演奏用のインターフェース規格で、シリアル接続でコントローラ、プレーヤ(音源)、鍵盤、を接続する規格です。ここでは、ノートオンコマンドを発生する簡単なコントローラを作成します。

    2. MIDIメッセージ
       midiのノートオンメッセージは、
       楽器の種類(音色)、音の高さ(音階)、音の強さ、
      を指定します。これを受けた音源は指定の音色、高さ、強さ、で音を出し始めます、
      音を終了するには、同じ音色、高さ、で強さ0のメッセージを送ります。
       具体的なメッセージの形式は

        0x9n,note,velocity

      です。0xは16進表記の意味です。n: はチャンネル番号で0..15がmidiのチャンネル1..16に対応します。midiチャンネルは楽器の音色に対応します。

      noteは音の高さでピアノ鍵盤の中央のド(c4)が60となります。半音を含めた鍵盤に通し番号をつけます。したがって、中央のレの音は62となります。

      velocity は音の強さを示し、0は無音、127をff(フォルテッシモ:最強)とします。

    3. 送信規則
       MIDIではこのメッセージの各バイトを、RS232Cと同じフォーマットでシリアル送信します。したがって、先頭に 0 のスタートビット、続けて 8ビット のメッセージバイト、最後に1ビットのストップビットを付加します。
       このシリアルデータを、ビット当たり32μSで送ります。したがって、1バイトは320μ秒で送ります。

    4. 送信ドライバ、レシーバ
       コネクタには5ピンDIN型コネクタを利用します。装置側はメス型で、ケーブルがオス型になります。電気的にはカレントループで送信側は、数mAのカレントループを構成します。0を送るには、4ピンから5ピンに数mAの電流を流せば良いのです。2ピンはケースグラウンドで、信号線のシールドを接続します。
       受信側では、4-5ピン間に光結合素子(フォトカプラ)を接続して信号を取り出します。したがって、ケーブルの信号線は2本のみです。



  2. ントローラの制作

    1. 回路
       MIDIコネクタに必要な信号端子は1本のみです。これにPICのPB1を利用します。また、監視用に PB2を利用します。これは、MIDIメッセージを送る毎に、点滅させます。

    2. プログラム
       PIC16F628や16F88には、シリアル送信機能がありますが、ここでは、練習のため?ソフトウエアで、RB1にシリアル信号を作成します。
       シリアル通信ではビットを送り出すタイミングが重要なので、ビット送信のタイミングはタイマー0の割り込みを利用します。
       main では、まず、タイマー0と割り込み設定を行います。タイマー0は OPTIOM レジスタで設定します(内部クロック、プリケーら)。割り込み(タイマー0)は INTCON レジスタで設定します。
       call note_on で MIDIメッセージを出力し、call T1SEC で少し休止してから、繰返しを行います。タイマー0はプリスケーラなしです。クロックは内部クロック4MHzを利用しますから、1μS間隔のタイマークロックになります。したがって、-32 をTIM0 に設定すると、32μS 後オーバーフロー割り込みを発生します。ここでは、32(10進)の値を bitDelay で定義しています。
       MIDIメッセージ送信後、動作確認のため、PB2に接続するLEDに点滅信号を送ります。

       noto_on では、ノートオンMIDIメッセージを発生します。midi_cmd から連続するバイトにMIDIメッセージを記録します。msg_lenにメッセージ長(3)を設定し、send_midi_msg で、midi_cmd から msg_len バイトのデータをシリアル送信します。

       send_midi_msgでは、1バイトの送信にタイマー0割り込みを利用します。割り込みを「可」とし、bit_cnt を9として割り込みによるバイト送信を開始します。send_wait のループで1バイト送信を待ち合わせます。送信が完了すると bit_cnt が 0x80 となります。

       ビット毎の送信は 4番地からの割り込み処理である intproc で行います。通常の割り込み処理では必須のWやステータスの保存、回復はここでは不要ですから省略しています。(32μ秒は32命令の余裕しかありません)。
       bit_cnt をチェックして、9ならスタートビット(0)、8-1 は sendByte を右シフトしながら送ります。最後は 1を送り、bit_cnt を 0x80 とします。これが送信完了の合図になります。

       TIM10からは、ソフトウエアによる時間待ちの処理です。T1SECで0.1秒程度の時間待ちを行います。この時間に特に意味はありません。

      ;;MIDI シリアルドライバ
      ;;MIDIのnoteONコマンドを送る
      ;;タイマー割込みを利用する
      ;;MIDI出力端子 RB1
      
      LIST P=16F648A, ST=OFF, R=DEC, F=INHX8M
      
      INCLUDE P16F648A.INC
      ;
      __CONFIG _INTRC_OSC_NOCLKOUT & _MCLRE_OFF  & _WDT_OFF & _PWRTE_ON 
      
      
      ;;; 定数
      ;WAIT_CNT       equ     0x20             ; 100usのwaitカウント
      bitDelay        equ     0x20             ; 31.25Kbit/s タイマーWait値(bit間隔 32us)
      ;FileRegi                       
      msg_len         equ     0x22
      bit_cnt         equ     0x23              ;割込み処理するビット数
      wsave           equ     0x24
      stsave          equ     0x25
      
      CNT1            equ     0x26
      CNT2            equ     0x27
      CNT3            equ     0x28
      sendByte        equ     0x29
      
      midi_cmd        equ     0x30              ; MIDIコマンド
      midi_note       equ     0x31
      midi_vel        equ     0x32
      
      
                      org     0
                      goto    main
      
                      org     4
                      goto    intproc
      
      main            
                      movlw   0x6                     ;初期設定
                      movwf   PORTB
                      bsf     STATUS,RP0              ;bank1
                      movlw   0xF0
                      movwf   TRISB
                      bcf     OPTION_REG,T0CS         ;内部クロックを利用
                      bsf     OPTION_REG,T0SE         ;タイマオーバーフロー割込み設定
                      bsf     OPTION_REG,PSA          ;プリスケーラを利用しない
                      movlw   0x80
                      movf    bit_cnt,F
                                      
                      bcf     STATUS,RP0              ;bank0
                      bsf     INTCON,T0IE             ;タイマ0割り込み許可、
                      ;bsf    PORTB,1                 ;MIDI出力H
                      ;bsf    PORTB,2         
                      ;BSF    INTCON,GIE
                                  
                              
      loop            call    note_on
      
                      bcf     PORTB,2                 ;Check LED
                      call    TIM100
                      bsf     PORTB,2
                      
                      call    T1SEC
                      goto    loop    
      
      ;;;;;;;;;;; note_on ;;;;;;;;;;;;;
      note_on                 
                      movlw   0x91
                      movwf   midi_cmd
                      movlw   0x3c                    ;note 3c
                      movwf   midi_note
                      movlw   0x64                    ;velocity
                      movwf   midi_vel
                      movlw   0x3                     ;message長
                      movwf   msg_len
                      call    send_midi_msg           ;midiメッセージ送信
                      return
      
      
      ;;;;;;;;;;; send_midi_msg ;;;;;;;;;;;;;
      ;; midi_msgをMIDI OUTに送信する
      send_midi_msg
                      movlw   midi_cmd
                      movwf   FSR
                      
                      movlw   0xff - bitDelay         
                      movwf   TMR0
                      bsf     INTCON,GIE              ;割込み可
      
      send_loop
                      movf    INDF,W
                      movwf   sendByte
                      incf    FSR,F
                      movlw   0x9
                      movwf   bit_cnt
                      
      
      send_wait
                      btfss   bit_cnt,7                ;送信完了を待つ
                      goto    send_wait
                      decfsz  msg_len,f                ;メッセージ長繰り返す
                      goto    send_loop
                      
                      bcf     INTCON,GIE              ;割込み停止
                      return  
      
                      
                      
      ;;;;;;;;;;;; 割り込み処理 intproc  ;;;;;;;;;;;;
      intproc                    ; 割り込み処理(MIDIバイトの送信処理)
      
                      ;movwf  wsave                   ; Wレジスタ退避
                      ;swapf  STATUS,W
                      ;movwf  stsave                  ; STATUS退避
                      bcf     INTCON,T0IF             ;割込み要求クリア
      
                      movlw   0x9                     ;スタートビット
                      subwf   bit_cnt,w
                      btfsc   STATUS,Z
                      goto    send_start              
                      
                      movlw   0x0                     ;ストップビット
                      subwf   bit_cnt,w
                      btfsc   STATUS,Z
                      goto    send_stop
                      
                      movlw   0x80                    ;ストップビット
                      subwf   bit_cnt,w
                      btfsc   STATUS,Z
                      goto    send_stop
                      
                      ;cje    bit_cnt,#9,send_strt
                      ;cje    bit_cnt,#0,send_stop
                      ;cje    bit_cnt,#0ffh,int_end
      
      
                      rrf     sendByte,f              ; データ部(8bit)送信 
                      btfss   STATUS,C
                      bcf     PORTB,1
                      btfsc   STATUS,C
                      bsf     PORTB,1
                      decf    bit_cnt,F       
                      goto    int_end
      
      send_start
                      bcf     PORTB,1                 ; Start bit
                      decf    bit_cnt,F                ; bit_cnt = 8
                      goto    int_end
      
      send_stop
                      bsf     PORTB,1                 ; Stop bit
                      movlw   0x80
                      movwf   bit_cnt                 ;send over
      
      int_end         ;; ----- 割り込み終了処理 -----
                      movlw   0xff-bitDelay
                      movwf   TMR0                    ; tmr0再設定(32us間隔)
                      
                      ;swapf  stsave,W                        ; STATUSを戻す
                      ;movwf  STATUS
                      ;swapf  wsave,F                 ; Wを戻す
                      ;swapf  wsave,W
                      retfie                          ; 復帰
      
      
      ;0.4msec Timer Subroutine
      TIM10
                      MOVLW    0F9H         ;249回
                      MOVWF    CNT1         ;ここまでで2サイクル
      TIMLP1          NOP
                      DECFSZ   CNT1,F       ;このループは4サイクル
                      GOTO     TIMLP1       ;2+4*249-1=997
                      RETURN                ;997+1=998*0.4usec=0.4msec
      ; 100msec Timer Subroutine
      TIM100
                      MOVLW    0F9H         ;249回 
                      MOVWF    CNT2
      TIMLP2
                      CALL     TIM10        ;2+(1000+3)*249-1=249748
                      DECFSZ   CNT2,F       ;このループは1回1003サイクル 
                      GOTO     TIMLP2
                      RETURN                ;+1=249749サイクル
      ;
      ;  1Sec Timer Subroutine
      ;
      T1SEC
                      MOVLW    0AH          ;10回
                      MOVWF    CNT3         ;100msec * 10
      T1LP
                      CALL     TIM100
                      DECFSZ   CNT3,F       ;このループは100msec
                      GOTO     T1LP
                      RETURN
      
                      END