ムライボックス (14) --- ソフトウェア (6)

今回は PIC16F18325 を使用した場合の話です。
ハードウェア回路としては 2017 年 12 月 12 日付けの記事 (→こちら ) に掲載してあります。
MPLAB X IDE に付属の MCC (MPLAB Code Configurator) を利用して作成しています。 プログラム・サイズは 820 ワード、RAM サイズは 171 バイトです。
MCC で生成されるファイルについては省略し、「main.c」の部分だけを下に示します。 (下のリストだけをコンパイルしても動作しません)
XG システム・エクスクルーシブ・メッセージを利用してリマップ/マスク・テーブルを書き換え可能にしてあります。 それについては次回に説明します。

#include "../mcc_generated_files/mcc.h"
#include <string.h>

// uncomment following line to inhibit ch. remapping
//#define REMAP_INH

// macro for "OR" mask output port polarity
#define MASK_POL(x) ((uint8_t)~(x))  // for '1' output = mask, '0' output = enable

// macro for USART RX FIFO buffer empty check
#define EUSART_rxbuf_not_empty (0 != eusartRxCount)

// "running" mask pattern
volatile uint8_t r_mask = 0x00;

uint8_t remap_tab[16] = {
  0,  // ch 1 --> ch 1 (1 origin) 
  0,  // ch 2 --> ch 1 
  0,  // ch 3 --> ch 1 
  0,  // ch 4 --> ch 1 
  4,  // ch 5,  no remap 
  5,  // ch 6,  no remap 
  6,  // ch 7,  no remap 
  7,  // ch 8,  no remap 
  8,  // ch 9,  no remap 
  9,  // ch 10, (drum ch), no remap 
  10, // ch 11, no remap 
  11, // ch 12, no remap 
  12, // ch 13, no remap 
  13, // ch 14, no remap 
  14, // ch 15, no remap 
  15  // ch 16, no remap 
}; // uint8_t remap_tab[]

uint8_t mask_tab[16] = {
  0x01, // ch 1  --> port 1
  0x02, // ch 2  --> port 2
  0x04, // ch 3  --> port 3
  0x08, // ch 4  --> port 4
  0x08, // ch 5  --> port 4
  0x08, // ch 6  --> port 4
  0x08, // ch 7  --> port 4
  0x08, // ch 8  --> port 4
  0x08, // ch 9  --> port 4
  0xff, // ch 10 --> all port (drum ch, broadcast)
  0x08, // ch 11 --> port 4
  0x08, // ch 12 --> port 4
  0x08, // ch 13 --> port 4
  0x08, // ch 14 --> port 4
  0x08, // ch 15 --> port 4
  0x08  // ch 16 --> port 4
}; // uint8_t mask_tab[]

//
// system common/realtime message to port mask pattern table
// port bitmap assignment:
// b15 = port16, ... , b1 = port2, b0 = port1
//
uint8_t sysmsg_tab[16] = {
  0xff, // 0xF0, Start of Exclusive (arbitrary)
  0xff, // 0xF1, MTC Quater frame (2 byte)
  0xff, // 0xF2, Song Position Pointer (3 byte)
  0xff, // 0xF3, Song Select (2 byte)
  0x00, // 0xF4, undefined
  0x00, // 0xF5, undefined
  0xff, // 0xF6, Tune Request (obsolute)
  0xff, // 0xF7, End of Exclusive
  0xff, // 0xF8, MIDI Clock
  0x00, // 0xF9, undefined
  0xff, // 0xFA, Start
  0xff, // 0xFB, Continue
  0xff, // 0xFC, Stop
  0x00, // 0xFD, undefined
  0xff, // 0xFE, Active sensing
  0x00  // 0xFF, System Reset (obsolute)
}; // uint8_t sysmsg_tab[]

void main( void )
{
  uint8_t c;       // received byte from MIDI-IN
  uint8_t midi_ch; // MIDI ch / sysmsg type
  uint8_t bm;      // port bitmask pattern
    
// initialize the device
  SYSTEM_Initialize();
// Enable the Global Interrupts
  INTERRUPT_GlobalInterruptEnable();
// Enable the Peripheral Interrupts
  INTERRUPT_PeripheralInterruptEnable();
  while (1) {
// test for EUSART TX shift reg empty and RX data ready
    if (TXSTAbits.TRMT && EUSART_rxbuf_not_empty) {
      c = EUSART_Read(); // get MIDI byte
      midi_ch = (0x0fu & c);  // extract MIDI ch / system msg type
      bm = r_mask; // use "running" port mask
      if (0x80u & c) { // status byte
        if (0xf0u > c) { // ch mode message [0x80..0xef]
          bm     = mask_tab[midi_ch]; // get port mask pattern  
          r_mask = bm; // set "running" port mask pattern 
#if !defined(REMAP_INH)        
          c = ((c & 0xf0u) | remap_tab[midi_ch]); // remap MIDI channel
#endif        
        } else { // system message
          bm = sysmsg_tab[midi_ch]; // escaping for system realtime
          if (0xf8u > c) { // system common msg [0xf0..0xf7]
            r_mask = bm; // set "running" port mask pattern
          } // if (0xf8u > c) { ...
        } // if (0xf0u > c) {} else { ...
      } else { // data byte				
//  no processing required for data byte
      } // if (0x80u & c) { ...
      bm = MASK_POL(bm); // adjust mask polarity
      CM1CON0bits.C1POL    = (0x01u & bm); // port 1 mask
      bm >>= 1; // next bit
      CM2CON0bits.C2POL    = (0x01u & bm); // port 2 mask
      bm >>= 1; // next bit
      PWM5CON0bits.PWM5POL = (0x01u & bm); // port 3 mask
      bm >>= 1; // next bit
      PWM6CON0bits.PWM6POL = (0x01u & bm); // port 4 mask
      TX1REG = c; // send MIDI byte
      sysx_proc(c);
    } else {
// EUSART TX busy or EUSART RX not ready        
    } // if (TXSTAbits.TRMT && ... ) { ...
  } // while (1) { ...
} // void main()

PIC16F18325 は 14 ピンのチップで、出力に使えるピン数も限られています。 CLC (Configurable Logic Cell) を利用して 4 ポートの出力を実現しており、それ以上の拡張は考えていないので、ビットマスク・パターンのビット幅を 16 ビットから 8 ビットに削減しています。
PIC の EUSART では、送信完了の割り込みはなく、ステータス・レジスタのフラグ・ビットをポーリングで監視する必要があります。
そのビットは、

    TXSTAbits.TRMT 

でアクセスできます。
このビットをメインの無限ループ

  while (1) {
   . . .
  }

の中でチェックしています。
単なる "OR" ゲートとして構成する CLC へのゲート出力にはコンパレータ・モジュール 1 および 2 の出力と、PWM モジュール 5 および 6 の出力とを使っています。
たとえばコンパレータ 1 では、出力極性を反転するビット

    CM1CON0bits.C1POL

を操作しています。
MCC で生成される割り込みを活用した EUSART 送受信ルーチンを使用しています。
Arduino/AVR の所で説明したように、送信側ルーチンは使わなければ何も影響を及ぼさないので、受信側のルーチンとバッファのみを利用しています。
「処理」および「"OR" ゲートへの出力」が済んで、UART 送信バッファへの書き込みも終了した時点、つまり、

    TX1REG = c; // send MIDI byte

の後の時点では、そのキャラクタが送信完了になるまで 1 キャラクタ・タイム = 約 0.3 ms の余裕があります。
つまり、そこはユーザ・インターフェース (UI) の処理やシステム・エクスクルーシブ処理を入れるのに適した場所となっています。
上のプログラムでも

    sysx_proc(c);

としてシステム・エクスクルーシブの処理を入れています。 (sysx_proc() 自体の記述は省略してあります)
ムライボックスとしての処理と重なる部分もありますが、ムライボックスの処理を優先としてムライボックスの処理の中には組み入れず、システム・エクスクルーシブ処理は別個に行なっています。
システム・エクスクルーシブ処理については次回説明します。