Thanks Thorsten. Here is my app.c
// $Id: app.c 1109 2010-10-11 23:14:49Z tk $
/*
* Example for a "fastscan button matrix"
*
* ==========================================================================
*
* Copyright © 2010 Thorsten Klose(tk@midibox.org)
* Licensed for personal non-commercial use only.
* All other rights reserved.
*
* ==========================================================================
*/
/////////////////////////////////////////////////////////////////////////////
// Include files
/////////////////////////////////////////////////////////////////////////////
#include <mios32.h>
#include "app.h"
#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
/////////////////////////////////////////////////////////////////////////////
// for optional debugging messages
/////////////////////////////////////////////////////////////////////////////
// level >= 1: print warnings (recommended default value)
// level >= 2: print debug messages for Robin's Fatar Keyboard
// level >= 3: print row/column messages in addition for initial testing of matrix scan for other usecases
#define DEBUG_VERBOSE_LEVEL 2
#define DEBUG_MSG MIOS32_MIDI_SendDebugMessage
/////////////////////////////////////////////////////////////////////////////
// Local definitions
/////////////////////////////////////////////////////////////////////////////
#define PRIORITY_TASK_MATRIX_SCAN ( tskIDLE_PRIORITY + 2 )
// scan 12 rows
#define MATRIX_NUM_ROWS 12
// sink drivers used? (no for Fatar keyboard)
#define MATRIX_DOUT_HAS_SINK_DRIVERS 0
// maximum number of supported pins (rowsxcolumns) = (12*16)
#define KEYBOARD_NUM_PINS (12*16)
// used MIDI port and channel (DEFAULT, USB0, UART0 or UART1)
#define KEYBOARD_MIDI_PORT UART0
//#define KEYBOARD_MIDI_CHN DEFAULT
/////////////////////////////////////////////////////////////////////////////
// Prototypes
/////////////////////////////////////////////////////////////////////////////
static void TASK_MatrixScan(void *pvParameters);
static void Timer(void);
/////////////////////////////////////////////////////////////////////////////
// Local Variables
/////////////////////////////////////////////////////////////////////////////
static u16 din_value[MATRIX_NUM_ROWS];
static volatile u32 timestamp;
static u32 kb_del_fast = 30;//used to set loudness resolution
static u32 kb_del_slow = 1000;//used to set loudness resolution
u8 toggle_state_pedal = 0;// using momentary action membrane switch, alternately selects soft or
//sostenuto pedal. Latter is grand piano type sustain
u8 toggle_state_port = 1;// using momentary action membrane switch, alternately USB0 or UART0
u8 toggle_state_swell = 1;// For Swell (Vol Control) Organ Presets
u8 hex_switch_state [8];
u8 kb_midi_port = 16;
s8 transpose = 0;
u8 kb_midi_chan = 0;
u8 prg = 0;
/////////////////////////////////////////////////////////////////////////////
// This hook is called after startup to initialize the application
/////////////////////////////////////////////////////////////////////////////
void APP_Init(void)
{
// initialize all LEDs
//MIOS32_BOARD_LED_Init(0xffffffff);
// initialize DIN arrays
int row;
for(row=0; row<MATRIX_NUM_ROWS; ++row) {
din_value[row] = 0xff; // default state: buttons depressed
}
// initialize all pins of J5C, as outputs in Push-Pull Mode
u8 pin;
for(pin=8; pin<11; ++pin){
MIOS32_BOARD_J5_PinInit(pin, MIOS32_BOARD_PIN_MODE_OUTPUT_PP);
MIOS32_BOARD_J5_PinSet(pin, 0);}// ensures initial state of LED's are off
// initialize all pins of J5A and J5B, as inputs in Pull-Up Mode
for(pin=0; pin<7; ++pin)
MIOS32_BOARD_J5_PinInit(pin, MIOS32_BOARD_PIN_MODE_INPUT_PU);
// initialize MIOS32 Timer #0, so that Timer() function is
// called each 100 uS:
MIOS32_TIMER_Init(0, 100, Timer, MIOS32_IRQ_PRIO_MID);
// start matrix scan task
xTaskCreate(TASK_MatrixScan, (signed portCHAR *)"MatrixScan", configMINIMAL_STACK_SIZE, NULL, PRIORITY_TASK_MATRIX_SCAN, NULL);
}
/////////////////////////////////////////////////////////////////////////////
// This timer function is called periodically each 100 uS
/////////////////////////////////////////////////////////////////////////////
static void Timer(void)
{
++timestamp;
}
/////////////////////////////////////////////////////////////////////////////
// This task is running endless in background
/////////////////////////////////////////////////////////////////////////////
void APP_Background(void)
{
// endless loop: print status information on LCD
while( 1 ) {
MIOS32_BOARD_LED_Init(0xffffffff);
// toggle the state of all LEDs (allows to measure the execution speed with a scope)
//MIOS32_BOARD_LED_Set(0xffffffff, ~MIOS32_BOARD_LED_Get());
}
}
/////////////////////////////////////////////////////////////////////////////
// This hook is called when a MIDI package has been received
/////////////////////////////////////////////////////////////////////////////
void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package)
{
}
/////////////////////////////////////////////////////////////////////////////
// This hook is called before the shift register chain is scanned
/////////////////////////////////////////////////////////////////////////////
void APP_SRIO_ServicePrepare(void)
{
}
/////////////////////////////////////////////////////////////////////////////
// This hook is called after the shift register chain has been scanned
/////////////////////////////////////////////////////////////////////////////
void APP_SRIO_ServiceFinish(void)
{
}
/////////////////////////////////////////////////////////////////////////////
// This hook is called when a button has been toggled
// pin_value is 1 when button released, and 0 when button pressed
/////////////////////////////////////////////////////////////////////////////
void APP_DIN_NotifyToggle(u32 pin, u32 pin_value)
{
}
/////////////////////////////////////////////////////////////////////////////
// This hook is called when an encoder has been moved
// incrementer is positive when encoder has been turned clockwise, else
// it is negative
/////////////////////////////////////////////////////////////////////////////
void APP_ENC_NotifyChange(u32 encoder, s32 incrementer)
{
}
/////////////////////////////////////////////////////////////////////////////
// This hook is called when a pot has been moved
/////////////////////////////////////////////////////////////////////////////
void APP_AIN_NotifyChange(u32 pin, u32 pin_value)
{
}
/////////////////////////////////////////////////////////////////////////////
// This task is called to scan the button matrix
/////////////////////////////////////////////////////////////////////////////
// will be called on button pin changes (see TASK_MatrixScan)
void BUTTON_NotifyToggle(u8 row, u8 column, u8 pin_value)
{
// determine pin number based on row/column
// based on pin map for Fatar keyboard provided by Robin (see doc/ directory)
// tested with utils/test_pinmap.pl
static u32 switch_timestamp[KEYBOARD_NUM_PINS];
u8 second_switch =0;
u8 send_note_on = 0;
u8 send_note_off = 0;
u32 delta = 0;
u8 note_number = 0;
int pin = 0;
//u8 hex_val = 0;
//left hand keys
if((column >=0 && column <=7 ) && (row >= 0 && row <= 9) && (row % 2 == 0)){//even
pin = 8 * row + 2 *(7 - column);}
if((column >=0 && column <=7 ) && (row>= 0 && row <= 9) && (row % 2 == 1)){//odd
pin = (8 * (row -1) + (2 * (7 - column)) + 1); }
//non-key switches
if((column >=0 && column <=7 ) && (row >= 10 && row <= 11)){
pin = 8* row + 7 - column;
// USB/ Midi Switch choose USB for PC and UART0 for external Midi Piano Module
if(pin == 95 && pin_value == 0){
toggle_state_port ^= 0x01;
MIOS32_BOARD_J5_PinSet(8,!toggle_state_port);
toggle_state_port == 1 ? (kb_midi_port = USB0) : (kb_midi_port = UART0);
}
// Hex Sw to determine midi channel
u32 hex_switch_state_index = pin - 80;
hex_switch_state[hex_switch_state_index] = (pin_value ==0) ? 1 :0;
if(pin >=80 && pin <= 87){
//Midi Channel set by Hex Switch
kb_midi_chan = ((hex_switch_state[0]) | ((hex_switch_state[1])<<1) | ((hex_switch_state[2])<<2)
|((hex_switch_state[3] )<<3));
//Program Change, Hex Switch used to provide 16 presets for Midi Piano Module with only 16 presets.
prg = ((hex_switch_state[4]) | ((hex_switch_state[5])<<1) | ((hex_switch_state[6])<<2)
|((hex_switch_state[7] )<<3));
//second hex switch to provide program change only for DIN0 Midi Out
if((toggle_state_port == 0))
MIOS32_MIDI_SendProgramChange(kb_midi_port, kb_midi_chan, prg);
}
// enable sustain pedal or sostenuto pedal Sw located on LH Panel
if(pin == 90 && pin_value == 0) {
toggle_state_pedal ^= 0x01;
MIOS32_BOARD_J5_PinSet(10,toggle_state_pedal);}
#if DEBUG_VERBOSE_LEVEL >= 2
DEBUG_MSG("toggle_state_port=%d, prg=%d, kb_midi_chan=%d, kb_midi_port=%d\n", toggle_state_port, prg, kb_midi_chan, kb_midi_port);
#endif
//soft pedal located on foot pedals
if(pin == 89) {// send soft pedal CC
MIOS32_MIDI_SendCC (kb_midi_port, kb_midi_chan, 67, pin_value == 0 ? 127 : 0);}
// sustain pedal located on foot pedals same switch as sostenuto below
if(pin == 88 && toggle_state_pedal == 0) { // send soft pedal CC
MIOS32_MIDI_SendCC (kb_midi_port, kb_midi_chan, 64, pin_value == 0 ? 127: 0) ;}
//sostenuto pedal located on foot pedals
if(pin == 89 && toggle_state_pedal == 1) { // send sostenuto pedal CC
MIOS32_MIDI_SendCC (kb_midi_port, kb_midi_chan, 66, pin_value == 0 ? 127 : 0 ) ;}
//Panic button, Midi All Notes Off No Sw yet
if(pin == 91 && pin_value == 0) {
MIOS32_MIDI_SendCC (kb_midi_port, kb_midi_chan, 123, 0);}
//transpose buttons
if(pin == 92 && pin_value == 0){//pin 92 is RH Transpose Button ie + Transpose (Higher Note)
transpose = transpose +1;}
if(pin == 93 && pin_value == 0){//pin 93 is LH Transpose Button ie + Transpose (Lower Note)
transpose = transpose -1;}
if(transpose > 12){ transpose = 12;}
if(transpose < -12){transpose = -12;}
// Presets Arrays
char * preset[15] = {"Dark Grand", "Classic Grand", "Mellow Ivory", "Rock Piano", "Warm Elect Piano",
"Studio Elec Piano", "B3 Organ", "Rock Organ", "Vibes ", "Bright Vibes ", "Strng Bass/Piano", "Elect Bass/Organ",
"Synth Tines", "Piano Bell", "Str Bass/E Piano"};
// Show transpose and Preset on LCD
MIOS32_LCD_Clear();
MIOS32_LCD_CursorSet(0,0);
MIOS32_LCD_PrintFormattedString ("%s", preset[prg]);
MIOS32_LCD_CursorSet(0,1);
MIOS32_LCD_PrintFormattedString ("TRANSPOSE %d",transpose);
//Swell (Volume) To enable Pot to control Midi Volume forFixed Volume Organ sounds
if(pin == 94 && pin_value == 0){
toggle_state_swell ^= 0x01;
MIOS32_BOARD_J5_PinSet(9,toggle_state_swell);}
//alter pot
#if DEBUG_VERBOSE_LEVEL >= 2
DEBUG_MSG(" transpose=%d, pin=%d, column=%d\n", transpose, pin, column);
#endif
}
//right hand keys
if((column >=8 && column <=15 ) && (row >= 0 && row <= 11) && (row % 2 == 0)){//even
pin = (8 * row + 2 * (15 - column ) + 96);}
if((column >=8 && column <=15 ) && (row>= 0 && row <= 11) && (row % 2 == 1)){//odd
pin = (8 * (row -1) + (2 * (15 - column ) ) + 1 +96); }
// following check ensures that we never continue with an unexpected/invalid pin number.
// e.g. this could address a memory location outside the last_timestamp[] array!
// print a warning message in this case for analysis purposes
if( pin < 0 || pin >= KEYBOARD_NUM_PINS ) {
#if DEBUG_VERBOSE_LEVEL >= 2
DEBUG_MSG("WARNING: row=0x%02x, column=0x%02x, pin_value=%d -> pin=%d NOT MAPPED!\n",
row, column, pin_value, pin);
#endif
return;
}
if(((column >=0 && column <=7 ) && (row >= 0 && row <= 9))
|| ((column >=8 && column <=15 ) && (row >= 0 && row <= 11))){
// first or second switch if a key?
second_switch = (pin & 1); // 0 if first switch, 1 if second switch
// the note number (starting from A-0 = 21) Note use of transpose variable
if((column >=0 && column <=7 ) && (row >= 0 && row <= 9)){//LH keys
note_number = transpose + 21 + (pin >> 1);}
if((column >=8 && column <=15 ) && (row >= 0 && row <= 11)){//RH keys
note_number = transpose + 13 + (pin >> 1);}
if( note_number > 127 ) // just another check to ensure that no invalid note will be sent
note_number = 127;
// we have three transitions which are for interest:
// a) first switch changes from 1->0 (pin_value == 0):
// - store the current timestamp
// b) second switch changes from 1->0 (pin_value == 0):
// - calculate delta between current timestamp and timestamp captured during a)
// - do this only if the captured timestamp is != 0 (see also c)
// - calculate velocity depending on the delay
// - send Note On event
// c) first switch changes from 0->1 (pin_value == 1):
// - send Note Off event (resp. Note On with velocity 0)
// - clear captured timestamp (allows to check for valid delay on next transition)
if( pin_value == 0 ) {
if( second_switch == 0 ) { // first switch
switch_timestamp[pin] = timestamp;
} else { // second switch
delta = timestamp - switch_timestamp[pin -1];
send_note_on = 1;
}
}
else {//pin_value = 1
if( second_switch == 1 ) { // 2nd switch
switch_timestamp[pin] = 0;
send_note_off = 1;
}
}
// now we know:
// - if a note on or off event should be sent
// - the measured delta (note on only)
if( send_note_on ) {
// determine velocity depending on delta
int velocity = 127 - (((delta -kb_del_fast) * 127) / (kb_del_slow-kb_del_fast));
// saturate to ensure that range 1..127 won't be exceeded
if( velocity < 1 )
velocity = 1;
if( velocity > 127 )
velocity = 127;
MIOS32_MIDI_SendNoteOn(kb_midi_port, kb_midi_chan, note_number, velocity);
#if DEBUG_VERBOSE_LEVEL >= 2
DEBUG_MSG("row=%d, column=%d, pin_value=%d -> pin=%d, timestamp=%u, delta=%d, velocity=%d,-> NOTE ON\n",
row, column, pin_value, pin, timestamp, delta, velocity);
#endif
} else if( send_note_off ) {
// send Note On with velocity 0
MIOS32_MIDI_SendNoteOn(kb_midi_port, kb_midi_chan, note_number, 0x00);
#if DEBUG_VERBOSE_LEVEL >= 2
DEBUG_MSG("row=%d, column=%d, pin_value=%d -> pin=%d, timestamp=%u -> NOTE OFF\n",
row, column, pin_value, pin, timestamp);
#endif
} else {
#if DEBUG_VERBOSE_LEVEL >= 2
DEBUG_MSG("row=%d, column=%d, pin_value=%d -> pin=%d, timestamp=%u -> IGNORE\n",
row, column, pin_value, pin, timestamp);
#endif
}
}
}
static void TASK_MatrixScan(void *pvParameters)
{
while( 1 ) {
// wait for next timesplice (1 mS)
//vTaskDelay(1 / portTICK_RATE_MS);
MIOS32_SPI_TransferModeInit(MIOS32_SRIO_SPI, MIOS32_SPI_MODE_CLK1_PHASE1, MIOS32_SPI_PRESCALER_8);
// 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);
}
}
}
}
}
}
Please note that I have added 16 diodes to the scan matrix: 4 to provide 16 Midi channels, 4 to provide 16 program changes plus others to change midi out channel, provide soft and sustain pedal and transpose + and -.
The debug messages need cleaning up. The LCD messages are placed in the body of the button_notify_toggle function.
Most of the complicated code is yours from a few years back.
Robin