MINO-o'-2016 9. FW(6)

昨日は帰るやいなやBTNQでした。

では続きです。(昨日の予定分だった)メインループ

  while(1){
    // check next cmd
    if( cmd_buffer_len == 0 ) {
      sleep_cpu();
      continue;
    }
    uint8_t cmd = cmd_buffer[cmd_buffer_head];
    cli();
    ++cmd_buffer_head;
    --cmd_buffer_len;
    sei();
    if( cmd < 0x10 ) { // keydown
      uint8_t key = current_octave * 12;
      key += cmd;
      uint8_t vel = current_adc;
      vel >>= 1;
      cli();
      uint8_t tail = send_buffer_head + send_buffer_len;
      send_buffer[tail++] = 0x80;
      send_buffer[tail++] = key;
      send_buffer[tail] = vel;
      send_buffer_len += 3;
      sei();
      key_value[cmd]=key;
    }
    else if( cmd < 0x20 ) { // keyup
      cmd -= 0x10;
      uint8_t key = key_value[cmd];
      cli();
      uint8_t tail = send_buffer_head + send_buffer_len;
      send_buffer[tail++] = 0x90;
      send_buffer[tail++] = key;
      send_buffer[tail] = 0;
      send_buffer_len += 3;
      sei();
      key_value[cmd]=0xff;
    }
    else if( cmd == CMD_KEY_OCTDOWN_DOWN ) {
      if ( current_octave ) --current_octave;
    }
    else if( cmd == CMD_KEY_OCTUP_DOWN ) {
      if ( current_octave < 9 ) ++current_octave;
    }
  }
}

淡々とコマンド処理、何もなければスリープです。
割り込み側とメイン側の両方で書き換えのあるキューのアクセスは割り込み禁止にして処理する必要があります。

次に、割り込み処理。

#define ENQUEUE_CMD_IF( SCANLINE, BIT, CMD_DOWN, CMD_UP )\
  do {\
    if( keyscan_a & (1<<(BIT))) {\
      uint8_t val=(keyscan_s[(SCANLINE)]&(1<<(BIT)))? (CMD_DOWN): (CMD_UP);\
      cmd_buffer[cmd_buffer_head+cmd_buffer_len] = val;\
      ++cmd_buffer_len;\
    }\
  } while(0);
#define LATCH595() do{\
  PORTB &= ~_BV(0);\
  _NOP();\
  PORTB |=  _BV(0);\
} while(0)

ISR( TIMER0_OVF_vect ) {
  static uint8_t keyscan_x[16];
  static uint8_t keyscan_s[4] = {0x1f,0x1f,0x1f,0x1f};
  static uint8_t scanidx = 0;
  uint8_t keys = PINC;
  static uint8_t ledcount = 0;

  current_adc = ADCH;
  ADCSRA |= 0x40;
  if(( UCSR0A & _BV(UDRE0)) && ( send_buffer_len > 0 )) {
    UDR0 = send_buffer[send_buffer_head];
    ++send_buffer_head;
    --send_buffer_len;
    PORTB |= _BV(1);
    ledcount = 32;
  }
  else {
    if( ledcount > 0 ) --ledcount;
    if( !ledcount ) PORTB &= ~_BV(1);
  }

  uint8_t scanline = scanidx & 3;
  keyscan_x[scanidx] = keys ^ keyscan_s[scanline];
  uint8_t keyscan_a;
  switch(scanline) {
case 0:
    PIND = 0x30;
    keyscan_a = keyscan_x[0] & keyscan_x[4] & keyscan_x[8] & keyscan_x[12];
    keyscan_s[0] ^= keyscan_a;

    ENQUEUE_CMD_IF( 0, 0, CMD_KEY_C_DOWN, CMD_KEY_C_UP );
    ENQUEUE_CMD_IF( 0, 1, CMD_KEY_E_DOWN, CMD_KEY_E_UP );
    ENQUEUE_CMD_IF( 0, 2, CMD_KEY_GS_DOWN, CMD_KEY_GS_UP );
    ENQUEUE_CMD_IF( 0, 3, CMD_KEY_C2_DOWN, CMD_KEY_C2_UP );

    LATCH595();
    SPSR;
    SPDR = display[current_octave*2+1];
    break;
case 1:
    PIND = 0x60;
    keyscan_a = keyscan_x[1] & keyscan_x[5] & keyscan_x[9] & keyscan_x[13];
    keyscan_s[1] ^= keyscan_a;

    ENQUEUE_CMD_IF( 1, 0, CMD_KEY_CS_DOWN, CMD_KEY_CS_UP );
    ENQUEUE_CMD_IF( 1, 1, CMD_KEY_F_DOWN, CMD_KEY_F_UP );
    ENQUEUE_CMD_IF( 1, 2, CMD_KEY_A_DOWN, CMD_KEY_A_UP );
    ENQUEUE_CMD_IF( 1, 3, CMD_KEY_OCTDOWN_DOWN, CMD_KEY_OCTDOWN_UP );

    SPSR;
    SPDR = DIGIT_C;
    break;
case 2:
    PIND = 0xc0;
    keyscan_a = keyscan_x[2] & keyscan_x[6] & keyscan_x[10] & keyscan_x[14];
    keyscan_s[2] ^= keyscan_a;

    ENQUEUE_CMD_IF( 2, 0, CMD_KEY_D_DOWN, CMD_KEY_D_UP );
    ENQUEUE_CMD_IF( 2, 1, CMD_KEY_FS_DOWN, CMD_KEY_FS_UP );
    ENQUEUE_CMD_IF( 2, 2, CMD_KEY_AS_DOWN, CMD_KEY_AS_UP );
    ENQUEUE_CMD_IF( 2, 3, CMD_KEY_OCTUP_DOWN, CMD_KEY_OCTUP_UP );

    LATCH595();
    SPSR;
    SPDR = display[current_octave*2];
    break;
case 3:
    PIND = 0x90;
    keyscan_a = keyscan_x[3] & keyscan_x[7] & keyscan_x[11] & keyscan_x[15];
    keyscan_s[3] ^= keyscan_a;

    ENQUEUE_CMD_IF( 3, 0, CMD_KEY_DS_DOWN, CMD_KEY_DS_UP );
    ENQUEUE_CMD_IF( 3, 1, CMD_KEY_G_DOWN, CMD_KEY_G_UP );
    ENQUEUE_CMD_IF( 3, 2, CMD_KEY_B_DOWN, CMD_KEY_B_UP );

    SPSR;
    SPDR = DIGIT_CAPO;
    break;
  }
  ++scanidx;
  scanidx &= 0x0f;
}

ちょっと長いですが、switch分岐がどれも似たようなものなので、読むのもそれほど苦でもないでしょう。キーの読みだし、MIDI送信キューからの送信、ADCの読みだし/次回実行、7セグの更新、キー状態が変化したらコマンドの発行、をしています。

なお、まだデバッグはされてません。