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セグの更新、キー状態が変化したらコマンドの発行、をしています。
なお、まだデバッグはされてません。