///////////////////////////////////////////////////////////////////////////// // Installed via MIOS32_MIDI_DirectRxCallback_Init ///////////////////////////////////////////////////////////////////////////// static s32 NOTIFY_MIDI_Rx(mios32_midi_port_t port, u8 midi_byte) { // check for MIDI clock if( midi_byte == 0xf8 ) { mios32_sys_time_t t = MIOS32_SYS_TimeGet(); u32 timestamp = 1000*t.seconds + t.fraction_ms; DelayUpdate(&d_tick, timestamp); if( (midi_clock_ctr % 24) == 0 ) DelayUpdate(&d_beat, timestamp); if( (midi_clock_ctr % 96) == 0 ) { DelayUpdate(&d_measure, timestamp); // force print message with each measure print_message = 1; } ++midi_clock_ctr; total_delay += d_tick.delay_last; return 0; // no error, no filtering } // check for MIDI start or continue if( midi_byte == 0xfa || midi_byte == 0xfb ) { mios32_sys_time_t t = MIOS32_SYS_TimeGet(); u32 timestamp = 1000*t.seconds + t.fraction_ms; timestamp_midi_start = timestamp; u8 including_min_max = 0; DelayInit(&d_tick, including_min_max); DelayInit(&d_measure, including_min_max); DelayInit(&d_beat, including_min_max); midi_clock_ctr = 0; total_delay = 0; return 0; // no error, no filtering } // check for MIDI stop if( midi_byte == 0xfc ) { // invalidate measured delays u8 including_min_max = 0; DelayInit(&d_tick, including_min_max); DelayInit(&d_measure, including_min_max); DelayInit(&d_beat, including_min_max); return 0; // no error, no filtering } return 0; // no error, no filtering }
///////////////////////////////////////////////////////////////////////////// // This task is running endless in background ///////////////////////////////////////////////////////////////////////////// void APP_Background(void) { int i; struct ntp_tm tm; const char *month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; const char *weekday_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; mios32_sys_time_t t; char timestring[64]; // clear LCD screen MIOS32_LCD_Clear(); // endless loop: print status information on LCD while( 1 ) { // new message requested? // TODO: add FreeRTOS specific queue handling! u8 new_msg = PRINT_MSG_NONE; portENTER_CRITICAL(); // port specific FreeRTOS function to disable tasks (nested) if( print_msg ) { new_msg = print_msg; print_msg = PRINT_MSG_NONE; // clear request } portEXIT_CRITICAL(); // port specific FreeRTOS function to enable tasks (nested) switch( new_msg ) { case PRINT_MSG_INIT: MIOS32_LCD_FontInit((u8 *)GLCD_FONT_NORMAL); MIOS32_LCD_CursorSet(0, 0); MIOS32_LCD_PrintString("see README.txt "); MIOS32_LCD_CursorSet(0, 1); MIOS32_LCD_PrintString("for details "); break; case PRINT_MSG_STATUS: { MIOS32_LCD_CursorSet(0, 0); // request status screen again (will stop once a new screen is requested by another task) print_msg = PRINT_MSG_STATUS; } break; } t = MIOS32_SYS_TimeGet(); // convert ntp seconds since 1900 into useful time structure ntp_tmtime(t.seconds, &tm); // honour dailight savings ntp_dst(&tm); MIOS32_LCD_FontInit((u8 *)GLCD_FONT_SMALL); MIOS32_LCD_CursorSet(15, 12); MIOS32_LCD_PrintFormattedString("%s %02d %s %04d %02d:%02d:%02d", weekday_names[tm.weekday], tm.day, month_names[tm.month-1], tm.year,tm.hour, tm.minute, tm.second); } }
// returns current timestamp static u32 tapTempoTimestamp(void) { #if 0 // Problem: LPC17 doesn't deliver a timestamp which is accurate enough... (only second resolution) mios32_sys_time_t t = MIOS32_SYS_TimeGet(); return 1000*t.seconds + t.fraction_ms; #else return seq_core_timestamp_ms; #endif }
s32 SEQ_TERMINAL_PrintSystem(void *_output_function) { void (*out)(char *format, ...) = _output_function; char str_buffer[128]; MUTEX_MIDIOUT_TAKE; out("System Informations:\n"); out("====================\n"); out(MIOS32_LCD_BOOT_MSG_LINE1 " " MIOS32_LCD_BOOT_MSG_LINE2 "\n"); mios32_sys_time_t t = MIOS32_SYS_TimeGet(); int hours = (t.seconds / 3600) % 24; int minutes = (t.seconds % 3600) / 60; int seconds = (t.seconds % 3600) % 60; out("Operating System: MIOS32\n"); out("Board: " MIOS32_BOARD_STR "\n"); out("Chip Family: " MIOS32_FAMILY_STR "\n"); if( MIOS32_SYS_SerialNumberGet((char *)str_buffer) >= 0 ) out("Serial Number: %s\n", str_buffer); else out("Serial Number: ?\n"); out("Flash Memory Size: %d bytes\n", MIOS32_SYS_FlashSizeGet()); out("RAM Size: %d bytes\n", MIOS32_SYS_RAMSizeGet()); out("Systime: %02d:%02d:%02d\n", hours, minutes, seconds); out("CPU Load: %02d%%\n", SEQ_STATISTICS_CurrentCPULoad()); out("MIDI Scheduler: Alloc %3d/%3d Drops: %3d", seq_midi_out_allocated, seq_midi_out_max_allocated, seq_midi_out_dropouts); u32 stopwatch_value_max = SEQ_STATISTICS_StopwatchGetValueMax(); u32 stopwatch_value = SEQ_STATISTICS_StopwatchGetValue(); if( stopwatch_value_max == 0xffffffff ) { out("Stopwatch: Overrun!\n"); } else if( !stopwatch_value_max ) { out("Stopwatch: no result yet\n"); } else { out("Stopwatch: %d/%d uS\n", stopwatch_value, stopwatch_value_max); } #if !defined(MIOS32_FAMILY_EMULATION) && configGENERATE_RUN_TIME_STATS // send Run Time Stats to MIOS terminal out("FreeRTOS Task RunTime Stats:\n"); FREERTOS_UTILS_RunTimeStats(); #endif out("done.\n"); MUTEX_MIDIOUT_GIVE; return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // Hack: reinitializes the screens after some time, to clean up // potentially garbled screens // ///////////////////////////////////////////////////////////////////////////// void reinit() { mios32_sys_time_t t = MIOS32_SYS_TimeGet(); charcount++; if (t.seconds > last_reinit_seconds || charcount > 500) { charcount = 0; last_reinit_seconds = t.seconds; MIOS32_BOARD_J15_RS_Set(0); MIOS32_BOARD_J15_RW_Set(0); APP_LCD_Cmd(0b00110011); APP_LCD_Cmd(0b00110010); APP_LCD_Cmd(0b00101000); // 4bit, 2 lines APP_LCD_Cmd(0b00001100); // display on, cursor off, char blinking off APP_LCD_Cmd(0b00000110); // entry mode } }
///////////////////////////////////////////////////////////////////////////// // Handles Idle Counter (frequently called from Background task) ///////////////////////////////////////////////////////////////////////////// s32 SEQ_STATISTICS_Idle(void) { static u32 idle_ctr = 0; static u32 last_seconds = 0; // determine the CPU load ++idle_ctr; mios32_sys_time_t t = MIOS32_SYS_TimeGet(); if( t.seconds != last_seconds ) { last_seconds = t.seconds; // MAX_IDLE_CTR defined in mios32_config.h // CPU Load is printed in main menu screen cpu_load_in_percent = 100 - ((100 * idle_ctr) / MAX_IDLE_CTR); #if 0 DEBUG_MSG("Load: %d%% (Ctr: %d)\n", cpu_load_in_percent, idle_ctr); #endif idle_ctr = 0; } return 0; // no error }
///////////////////////////////////////////////////////////////////////////// // Local button callback function // Should return: // 1 if value has been changed // 0 if value hasn't been changed // -1 if invalid or unsupported button ///////////////////////////////////////////////////////////////////////////// static s32 Button_Handler(seq_ui_button_t button, s32 depressed) { if( SEQ_FILE_FormattingRequired() ) return 0; // no button action as long as files not available u8 group; s32 status; switch( button ) { case SEQ_UI_BUTTON_Edit: { if ( depressed && (selected_page == PAGE_REMIX) ) { // we want to show vertical VU meters normal mode SEQ_LCD_InitSpecialChars(SEQ_LCD_CHARSET_VBars); selected_page = PAGE_MAIN; // // here we run the commands for remix states // u16 remix_map_tmp; // if we are in no preview_mode them call the demix procedment in case we got some request if ( (seq_pattern_remix_map != seq_pattern_demix_map) && ( remix_mode ) ) { remix_map_tmp = seq_pattern_remix_map; seq_pattern_remix_map = ~(seq_pattern_remix_map ^ seq_pattern_demix_map); if (ableton_api) { // TODO: implements a ableton remotescript to the clip control functionality u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) { // if we got the track bit setup inside our remix_map, them do not send mixer data for that track channel, let it be mixed down if ( ((1 << track) | seq_pattern_remix_map) == seq_pattern_remix_map ) { // do nothing... } else { // delay our task for ableton get a breath before change the pattern line vTaskDelay(50); // send slot play envet to ableton: cc (111 + track) with value 127 to channel 16 MIOS32_MIDI_SendCC(ableton_port, 15, (111 + track), 127); } } } // if we are in remix mode lets start the process if (remix_map_tmp == 0) { //pattern_name = pattern_name_remix; preview_mode = 0; remix_mode = 0; // set the pattern_remix_timer reference pattern_remix_timer = MIOS32_SYS_TimeGet(); } // Change Pattern u8 group; for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { // change pattern SEQ_PATTERN_Change(group, seq_pattern[group], 0); } // copy the pattern name for future reference sprintf(pattern_name, seq_pattern_name[0]); // getting back seq_pattern_remix_map = remix_map_tmp; seq_pattern_demix_map = seq_pattern_remix_map; } } else if ( !depressed ) { // we want to show horizontal VU meters in remix mode SEQ_LCD_InitSpecialChars(SEQ_LCD_CHARSET_HBars); selected_page = PAGE_REMIX; } return 1; } break; case SEQ_UI_BUTTON_Select: { if(!depressed) { selected_page = PAGE_OPTION; return 1; } if( (depressed) && (selected_page == PAGE_OPTION) ) { selected_page = PAGE_MAIN; } } break; default: break; } if( depressed ) return 0; // ignore when button depressed if its not SEQ_UI_BUTTON_Select or SEQ_UI_BUTTON_Edit switch( selected_page ) { /////////////////////////////////////////////////////////////////////////// // REMIX PAGE case PAGE_REMIX: { if( button <= SEQ_UI_BUTTON_GP16 ) { // re-using encoder routine return Encoder_Handler(button, 0); } } break; /////////////////////////////////////////////////////////////////////////// // OPTION PAGE case PAGE_OPTION: { switch( button ) { case SEQ_UI_BUTTON_GP1: // Save Pattern(all 4 groups at once) for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { if( (status=SEQ_PATTERN_Save(group, seq_pattern[group])) < 0 ) { SEQ_UI_SDCardErrMsg(2000, status); return 0; } } // Save the pattern mixer SEQ_MIXER_Save(SEQ_MIXER_NumGet()); break; case SEQ_UI_BUTTON_GP2: // Edit Pattern Name //SEQ_LCD_Clear(); selected_page = PAGE_NAME_EDIT; break; case SEQ_UI_BUTTON_GP3: // Copy Pattern // We are going to use the multicopy procedure SEQ_UI_PATTERN_Copy(); // coping mixer SEQ_UI_MIXER_Copy(); break; case SEQ_UI_BUTTON_GP4: // Paste Pattern // We are going to use the multicopy procedure SEQ_UI_PATTERN_Paste(); // paste mixer SEQ_UI_MIXER_Paste(); break; case SEQ_UI_BUTTON_GP5: case SEQ_UI_BUTTON_GP6: return -1; // not mapped case SEQ_UI_BUTTON_GP7: case SEQ_UI_BUTTON_GP8: // Track Delay Page //SEQ_LCD_Clear(); //selected_page = PAGE_TRK_DELAY; //break; return -1; // not mapped case SEQ_UI_BUTTON_GP9: case SEQ_UI_BUTTON_GP10: return -1; // not mapped case SEQ_UI_BUTTON_GP11: case SEQ_UI_BUTTON_GP12: return -1; // not mapped case SEQ_UI_BUTTON_GP13: case SEQ_UI_BUTTON_GP14: // Auto Save switch auto_save ^= 1; break; break; case SEQ_UI_BUTTON_GP15: case SEQ_UI_BUTTON_GP16: // Ableton API switch ableton_api ^= 1; return -1; // not mapped } return 0; } break; /////////////////////////////////////////////////////////////////////////// // PATTERN NAME EDIT PAGE case PAGE_NAME_EDIT: { return Encoder_Handler((seq_ui_encoder_t)button, 0); } break; /////////////////////////////////////////////////////////////////////////// // TRACK DELAY PAGE case PAGE_TRK_DELAY: { } break; /////////////////////////////////////////////////////////////////////////// // MAIN PAGE case PAGE_MAIN: { // Pattern Change Group Requests if( button <= SEQ_UI_BUTTON_GP8 ) { u8 group; for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { if( selected_pattern[group].group == button ) { if( SEQ_FILE_B_NumPatterns(selected_pattern[group].bank) > 64 ) { selected_pattern[group].lower ^= 1; } else { selected_pattern[group].lower = 0; // toggling not required } } else { selected_pattern[group].group = button; preview_bank = button; //preview_mode = 0; } } SEQ_PATTERN_PeekName(selected_pattern[0], preview_name); preview_mode = 1; return 1; // value always changed } // Pattern Change Request if( button >= SEQ_UI_BUTTON_GP9 && button <= SEQ_UI_BUTTON_GP16 ) { //u8 group; s32 status = 0; // setting save patterns for later autosave procedure //for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) // save_pattern[group] = seq_pattern[group]; // change preview pattern selected_pattern[0].num = button-8; selected_pattern[1].num = button-8; selected_pattern[2].num = button-8; selected_pattern[3].num = button-8; if ( !preview_mode ) { preview_num = button-8; SEQ_PATTERN_PeekName(selected_pattern[0], preview_name); preview_mode = 1; // for security reasons in live environment, we can not accept rapid 2x press of pattern button. // if the buttons are not good quality they can send a 2x rapid signal os pressing in just one physical pressing // the time we need is bassicly the human time to read the name of patterns requested // start time in count pc_time_control = seq_core_timestamp_ms + 200; // vTaksDelay(200); } else { // if ( remix_mode == 0 ) { //char str[30]; //sprintf(str, "%d = %d %d = %d", selected_pattern[0].num, preview_num, selected_pattern[0].group, preview_bank); //SEQ_UI_Msg(SEQ_UI_MSG_USER, 2000, str, "debug"); // Check for remix state if ( (selected_pattern[0].num == preview_num) && (selected_pattern[0].group == preview_bank) ) { //if ( pc_time_control >= seq_core_timestamp_ms ) // return 0; // do nothing, just to be shure we are not handle acidental double clicks, it happens! // if we got some remix bit setup, enter in remix mode if (seq_pattern_remix_map != 0) { // set the remix mode remix_mode = 1; } } } // Change Pattern if ( (selected_pattern[0].num == preview_num) && (selected_pattern[0].group == preview_bank) ) { if ( pc_time_control >= seq_core_timestamp_ms ) return 0; // do nothing, just to be shure we are not handle acidental double clicks if (ableton_api) { u8 ableton_pattern_number = 0; // // ABLETON API PATTERN CHANGE HANDLE // // send ableton event to change the line clips if (selected_pattern[0].lower) { ableton_pattern_number = ((selected_pattern[0].group - 1) * 8) + button; } else { ableton_pattern_number = (((selected_pattern[0].group - 1) + 8) * 8) + button; } // midi_cc to send = 110 // midi_chn = 16 // midi_port = ableton_port // here we need to send a clip line change request MIOS32_MIDI_SendCC(ableton_port, 15, 110, ableton_pattern_number); // delay our task for ableton get a breath before change the pattern line vTaskDelay(50); // clip triggering u8 track; for(track=0; track<SEQ_CORE_NUM_TRACKS; ++track) { // if we got the track bit setup inside our remix_map, them do not send mixer data for that track channel, let it be mixed down if ( ((1 << track) | seq_pattern_remix_map) == seq_pattern_remix_map ) { // do nothing... } else { // delay our task for ableton get a breath before change the pattern line vTaskDelay(50); // send clip slot play envet to ableton cc (111 + track) with value 127 to channel 16 MIOS32_MIDI_SendCC(ableton_port, 15, (111 + track), 127); } } // send a global clip change(all clips in the clip line) // send play envet to ableton cc 20 with value 127 to channel 16 //MIOS32_MIDI_SendCC(ableton_port, 15, 20, 127); } // // Autosave Pattern // if (auto_save) { // Autosave all 4 group of patterns for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { // Save to SD Card if( (status=SEQ_PATTERN_Save(group, seq_pattern[group])) < 0 ) { SEQ_UI_SDCardErrMsg(2000, status); } } // Autosave Mixer Map SEQ_MIXER_Save(SEQ_MIXER_NumGet()); } // if we are not in remix mode if (seq_pattern_remix_map == 0) { // keep the name of old pattern(because we still got 1 or more tracks belongs to this pattern) //pattern_name = seq_pattern_name; // set timer for mixed pattern pattern_remix_timer = MIOS32_SYS_TimeGet(); } // // Pattern change request // for(group=0; group<SEQ_CORE_NUM_GROUPS; ++group) { // change pattern //selected_pattern[group].num = button-8; SEQ_PATTERN_Change(group, selected_pattern[group], 0); //if ( remix_mode ) { // keep the pattern name... //sprintf(seq_pattern_name[group], pattern_name); //} } if ( !remix_mode ) { // copy the pattern name for future reference //sprintf(pattern_name, seq_pattern_name[0]); SEQ_PATTERN_PeekName(selected_pattern[0], pattern_name); // TODO: sync this with the pattern change handled by SEQ_PATTERN_Handler() //pattern_timer.seconds = 0; preview_mode = 0; } // support for demix seq_pattern_demix_map = seq_pattern_remix_map; //preview_mode = 0; // Request a Pattern Preview } else { preview_num = button-8; SEQ_PATTERN_PeekName(selected_pattern[0], preview_name); // for security reasons in live environment, we can not accept 2x rapid press of pattern button. // if the buttons are not good quality they can send a 2x rapid signal os pressing in just one physical pressing // the time we need is bassicly the human time to read the name of patterns requested // start time in count pc_time_control = seq_core_timestamp_ms + 200; } } return 1; // value always changed } u8 visible_track = SEQ_UI_VisibleTrackGet(); switch( button ) { case SEQ_UI_BUTTON_Right: // switch to next track if( visible_track < (SEQ_CORE_NUM_TRACKS - 1) ) visible_track++; ui_selected_tracks = (1 << visible_track); ui_selected_group = visible_track / 4; return 1; // value always changed case SEQ_UI_BUTTON_Left: // switch to previous track if( visible_track >= 1 ) visible_track--; ui_selected_tracks = (1 << visible_track); ui_selected_group = visible_track / 4; return 1; // value always changed case SEQ_UI_BUTTON_Up: return Encoder_Handler(SEQ_UI_ENCODER_Datawheel, 1); case SEQ_UI_BUTTON_Down: return Encoder_Handler(SEQ_UI_ENCODER_Datawheel, -1); } return -1; // invalid or unsupported button } break; /////////////////////////////////////////////////////////////////////////// // Page do not exist default: break; } return 0; }
///////////////////////////////////////////////////////////////////////////// // Local Display Handler function // IN: <high_prio>: if set, a high-priority LCD update is requested ///////////////////////////////////////////////////////////////////////////// static s32 LCD_Handler(u8 high_prio) { if( high_prio ) { SEQ_LCD_CursorSet(32, 0); mios32_sys_time_t t = MIOS32_SYS_TimeGet(); int hours = (t.seconds / 3600) % 24; int minutes = (t.seconds % 3600) / 60; int seconds = (t.seconds % 3600) % 60; SEQ_LCD_PrintFormattedString("%02d:%02d:%02d", hours, minutes, seconds); return 0; } // layout: // 00000000001111111111222222222233333333330000000000111111111122222222223333333333 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789 // <--------------------------------------><--------------------------------------> // About this MIDIbox: 00:00:00Stopwatch: 100/ 300 uS CPU Load: 40% // System Globals Tracks TrackInfo>MIDI Scheduler: Alloc 0/ 0 Drops: 0 // Select MIDI Device with GP Button: 10 Devices found under /sysex // xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx> /////////////////////////////////////////////////////////////////////////// SEQ_LCD_CursorSet(0, 0); SEQ_LCD_PrintString("About this MIDIbox: "); /////////////////////////////////////////////////////////////////////////// SEQ_LCD_CursorSet(0, 1); SEQ_LCD_PrintList((char *)ui_global_dir_list, LIST_ENTRY_WIDTH, NUM_LIST_ITEMS, NUM_LIST_DISPLAYED_ITEMS, ui_selected_item, list_view_offset); /////////////////////////////////////////////////////////////////////////// switch( ui_selected_item + list_view_offset ) { case LIST_ITEM_SYSTEM: { u32 stopwatch_value_max = SEQ_STATISTICS_StopwatchGetValueMax(); u32 stopwatch_value = SEQ_STATISTICS_StopwatchGetValue(); SEQ_LCD_CursorSet(40, 0); if( stopwatch_value_max == 0xffffffff ) { SEQ_LCD_PrintFormattedString("Stopwatch: Overrun! "); } else if( !stopwatch_value_max ) { SEQ_LCD_PrintFormattedString("Stopwatch: no result yet"); } else { SEQ_LCD_PrintFormattedString("Stopwatch: %4d/%4d uS ", stopwatch_value, stopwatch_value_max); } SEQ_LCD_PrintSpaces(3); SEQ_LCD_PrintFormattedString("CPU Load: %02d%%", SEQ_STATISTICS_CurrentCPULoad()); SEQ_LCD_CursorSet(40, 1); if( seq_midi_out_allocated > 1000 || seq_midi_out_max_allocated > 1000 || seq_midi_out_dropouts > 1000 ) { SEQ_LCD_PrintFormattedString("MIDI Scheduler: Alloc %4d/%4d Dr: %4d", seq_midi_out_allocated, seq_midi_out_max_allocated, seq_midi_out_dropouts); } else { SEQ_LCD_PrintFormattedString("MIDI Scheduler: Alloc %3d/%3d Drops: %3d", seq_midi_out_allocated, seq_midi_out_max_allocated, seq_midi_out_dropouts); } } break; default: SEQ_LCD_CursorSet(40, 0); // <----------------------------------------> SEQ_LCD_PrintString("Press SELECT to send Informations "); SEQ_LCD_CursorSet(40, 1); SEQ_LCD_PrintString("to MIOS Terminal "); } return 0; // no error }
static void TASK_MatrixScan(void *pvParameters) { while( 1 ) { // wait for next timesplice (1 mS) vTaskDelay(1 / portTICK_RATE_MS); // determine timestamp (we need it for delay measurements) mios32_sys_time_t t = MIOS32_SYS_TimeGet(); u32 timestamp = 1000*t.seconds + t.fraction_ms; // loop: // - latch DIN/DOUT values // - shift selection pattern for *next* row to DOUT registers // - read DIN values of previously selected row // since we need to select the first row before the first DIN values are latched, we loop from -1 // to handle the initial state int row; for(row=-1; row<MATRIX_NUM_ROWS; ++row) { if( row >= 0 ) { // not required for initial scan // latch DIN values MIOS32_SPI_RC_PinSet(MIOS32_SRIO_SPI, MIOS32_SRIO_SPI_RC_PIN, 0); // spi, rc_pin, pin_value MIOS32_DELAY_Wait_uS(1); MIOS32_SPI_RC_PinSet(MIOS32_SRIO_SPI, MIOS32_SRIO_SPI_RC_PIN, 1); // spi, rc_pin, pin_value } // determine selection mask for next row (written into DOUT registers while reading DIN registers) u16 select_row_pattern = ~(1 << (row+1)); #if MATRIX_DOUT_HAS_SINK_DRIVERS select_row_pattern ^= 0xffff; // invert selection pattern if sink drivers are connected to DOUT pins #endif // read DIN, write DOUT u8 din0 = MIOS32_SPI_TransferByte(MIOS32_SRIO_SPI, (select_row_pattern >> 8) & 0xff); u8 din1 = MIOS32_SPI_TransferByte(MIOS32_SRIO_SPI, (select_row_pattern >> 0) & 0xff); // latch new DOUT value MIOS32_SPI_RC_PinSet(MIOS32_SRIO_SPI, MIOS32_SRIO_SPI_RC_PIN, 0); // spi, rc_pin, pin_value MIOS32_DELAY_Wait_uS(1); MIOS32_SPI_RC_PinSet(MIOS32_SRIO_SPI, MIOS32_SRIO_SPI_RC_PIN, 1); // spi, rc_pin, pin_value if( row >= 0 ) { // combine read DIN bytes to 16bit value u16 din_pattern = (din1 << 8) | din0; // check if values have been changed via XOR combination with previously scanned value u16 changed = din_pattern ^ din_value[row]; if( changed ) { // store changed value din_value[row] = din_pattern; // notify changed value int column; for(column=0; column<16; ++column) { u16 mask = 1 << column; if( changed & mask ) BUTTON_NotifyToggle(row, column, (din_pattern & mask) ? 1 : 0, timestamp); } } } } } }