Subversion Repositories svn.mios32

Rev

Rev 1109 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1086 tk 1
// $Id: app.c 2425 2016-11-03 00:44:22Z tk $
2
/*
3
 * Example for a "fastscan button matrix"
4
 *
5
 * ==========================================================================
6
 *
7
 *  Copyright (C) 2010 Thorsten Klose(tk@midibox.org)
8
 *  Licensed for personal non-commercial use only.
9
 *  All other rights reserved.
10
 *
11
 * ==========================================================================
12
 */
13
 
14
/////////////////////////////////////////////////////////////////////////////
15
// Include files
16
/////////////////////////////////////////////////////////////////////////////
17
 
18
#include <mios32.h>
19
#include "app.h"
20
 
21
#include <FreeRTOS.h>
22
#include <task.h>
23
#include <queue.h>
24
 
25
 
26
/////////////////////////////////////////////////////////////////////////////
27
// for optional debugging messages
28
/////////////////////////////////////////////////////////////////////////////
29
 
1109 tk 30
// level >= 1: print warnings (recommented default value)
31
// level >= 2: print debug messages for Robin's Fatar Keyboard
32
// level >= 3: print row/column messages in addition for initial testing of matrix scan for other usecases
33
#define DEBUG_VERBOSE_LEVEL 1
1086 tk 34
#define DEBUG_MSG MIOS32_MIDI_SendDebugMessage
35
 
36
/////////////////////////////////////////////////////////////////////////////
37
// Local definitions
38
/////////////////////////////////////////////////////////////////////////////
39
 
40
#define PRIORITY_TASK_MATRIX_SCAN   ( tskIDLE_PRIORITY + 2 )
41
 
42
// scan 16 rows
43
#define MATRIX_NUM_ROWS 16
44
 
1096 tk 45
// sink drivers used? (no for Fatar keyboard)
46
#define MATRIX_DOUT_HAS_SINK_DRIVERS 0
47
 
1092 tk 48
// maximum number of supported keys (rowsxcolumns = 16*16)
49
#define KEYBOARD_NUM_PINS (16*16)
50
 
1096 tk 51
// used MIDI port and channel (DEFAULT, USB0, UART0 or UART1)
52
#define KEYBOARD_MIDI_PORT DEFAULT
53
#define KEYBOARD_MIDI_CHN  Chn1
1086 tk 54
 
1109 tk 55
// initial minimum/maximum delay to calculate velocity
56
// (will be copied into variables, so that values could be changed during runtime)
57
#define INITIAL_KEYBOARD_DELAY_FASTEST 4
58
#define INITIAL_KEYBOARD_DELAY_SLOWEST 200
1086 tk 59
 
1096 tk 60
 
1086 tk 61
/////////////////////////////////////////////////////////////////////////////
62
// Prototypes
63
/////////////////////////////////////////////////////////////////////////////
64
static void TASK_MatrixScan(void *pvParameters);
65
 
66
 
67
/////////////////////////////////////////////////////////////////////////////
68
// Local Variables
69
/////////////////////////////////////////////////////////////////////////////
70
 
1109 tk 71
static u16 din_value[MATRIX_NUM_ROWS];
1086 tk 72
 
1109 tk 73
static u32 last_timestamp[KEYBOARD_NUM_PINS];
1086 tk 74
 
1109 tk 75
static u32 keyboard_delay_fastest;
76
static u32 keyboard_delay_slowest;
1092 tk 77
 
1086 tk 78
/////////////////////////////////////////////////////////////////////////////
79
// This hook is called after startup to initialize the application
80
/////////////////////////////////////////////////////////////////////////////
81
void APP_Init(void)
82
{
83
  // initialize all LEDs
84
  MIOS32_BOARD_LED_Init(0xffffffff);
85
 
86
  // initialize DIN arrays
87
  int row;
88
  for(row=0; row<MATRIX_NUM_ROWS; ++row) {
89
    din_value[row] = 0xffff; // default state: buttons depressed
90
  }
91
 
1092 tk 92
  // initialize timestamps
93
  int i;
94
  for(i=0; i<KEYBOARD_NUM_PINS; ++i) {
95
    last_timestamp[i] = 0;
96
  }
1086 tk 97
 
1109 tk 98
  // initialize keyboard delay values
99
  keyboard_delay_fastest = INITIAL_KEYBOARD_DELAY_FASTEST;
100
  keyboard_delay_slowest = INITIAL_KEYBOARD_DELAY_SLOWEST;
1103 tk 101
 
1086 tk 102
  // start matrix scan task
2425 tk 103
  xTaskCreate(TASK_MatrixScan, "MatrixScan", configMINIMAL_STACK_SIZE, NULL, PRIORITY_TASK_MATRIX_SCAN, NULL);
1086 tk 104
}
105
 
106
 
107
/////////////////////////////////////////////////////////////////////////////
108
// This task is running endless in background
109
/////////////////////////////////////////////////////////////////////////////
110
void APP_Background(void)
111
{
112
  // endless loop
113
  while( 1 ) {
114
    // toggle the state of all LEDs (allows to measure the execution speed with a scope)
115
    MIOS32_BOARD_LED_Set(0xffffffff, ~MIOS32_BOARD_LED_Get());
116
 
117
  }
118
}
119
 
120
 
121
/////////////////////////////////////////////////////////////////////////////
122
// This hook is called when a MIDI package has been received
123
/////////////////////////////////////////////////////////////////////////////
124
void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package)
125
{
126
}
127
 
128
 
129
/////////////////////////////////////////////////////////////////////////////
130
// This hook is called before the shift register chain is scanned
131
/////////////////////////////////////////////////////////////////////////////
132
void APP_SRIO_ServicePrepare(void)
133
{
134
}
135
 
136
 
137
/////////////////////////////////////////////////////////////////////////////
138
// This hook is called after the shift register chain has been scanned
139
/////////////////////////////////////////////////////////////////////////////
140
void APP_SRIO_ServiceFinish(void)
141
{
142
}
143
 
144
 
145
/////////////////////////////////////////////////////////////////////////////
146
// This hook is called when a button has been toggled
147
// pin_value is 1 when button released, and 0 when button pressed
148
/////////////////////////////////////////////////////////////////////////////
149
void APP_DIN_NotifyToggle(u32 pin, u32 pin_value)
150
{
151
}
152
 
153
 
154
/////////////////////////////////////////////////////////////////////////////
155
// This hook is called when an encoder has been moved
156
// incrementer is positive when encoder has been turned clockwise, else
157
// it is negative
158
/////////////////////////////////////////////////////////////////////////////
159
void APP_ENC_NotifyChange(u32 encoder, s32 incrementer)
160
{
161
}
162
 
163
 
164
/////////////////////////////////////////////////////////////////////////////
165
// This hook is called when a pot has been moved
166
/////////////////////////////////////////////////////////////////////////////
167
void APP_AIN_NotifyChange(u32 pin, u32 pin_value)
168
{
169
}
170
 
171
 
172
 
173
/////////////////////////////////////////////////////////////////////////////
174
// This task is called each mS to scan the button matrix
175
/////////////////////////////////////////////////////////////////////////////
176
 
1106 tk 177
// will be called on button pin changes (see TASK_BLM_Check)
1092 tk 178
void BUTTON_NotifyToggle(u8 row, u8 column, u8 pin_value, u32 timestamp)
1086 tk 179
{
1092 tk 180
  // determine pin number based on row/column
1103 tk 181
  // based on pin map for fadar keyboard provided by Robin (see doc/ directory)
1092 tk 182
  // tested with utils/test_pinmap.pl
183
 
1109 tk 184
#if DEBUG_VERBOSE_LEVEL >= 3
185
    DEBUG_MSG("row=0x%02x, column=0x%02x, pin_value=%d\n",
186
          row, column, pin_value);
187
#endif
188
 
1092 tk 189
  int pin = -1;
190
 
191
  // pin number (counted from 0) consists of:
1103 tk 192
  //   bit #0 if row -> pin bit #0
193
  int bit0 = row & 1;
1094 tk 194
  //   bit #2..0 of column -> pin bit #3..1
195
  int bit3to1 = column & 0x7;
1103 tk 196
  //   bit #3..1 of row -> pin bit #6..4
197
  int bit6to4 = (row & 0xe) >> 1;
1092 tk 198
 
199
  // combine to pin value
200
  if( column < 8 ) {
201
    // left half
1103 tk 202
    if( row >= 0 && row <= 0x9 ) {
1092 tk 203
      pin = bit0 | (bit6to4 << 4) | (bit3to1 << 1);
204
    }
205
  } else {
206
    // right half
1104 tk 207
    if( row >= 0 && row <= 0xb ) {
1092 tk 208
      pin = 80 + (bit0 | (bit6to4 << 4) | (bit3to1 << 1));
209
    }
210
  }
211
 
1096 tk 212
  // following check ensures that we never continue with an unexpected/invalid pin number.
213
  // e.g. this could address a memory location outside the last_timestamp[] array!
214
  // print a warning message in this case for analysis purposes
1097 tk 215
  if( pin < 0 || pin >= KEYBOARD_NUM_PINS ) {
1096 tk 216
#if DEBUG_VERBOSE_LEVEL >= 1
1101 tk 217
    DEBUG_MSG("WARNING: row=0x%02x, column=0x%02x, pin_value=%d -> pin=%d NOT MAPPED!\n",
1103 tk 218
          row, column, pin_value, pin);
1096 tk 219
#endif
220
    return;
221
  }
222
 
223
  // first or second switch if a key?
224
  u8 second_switch = (pin & 1); // 0 if first switch, 1 if second switch
225
 
1103 tk 226
  // the note number (starting from A-0 = 0x15)
227
  int note_number = 0x15 + (pin >> 1);
1096 tk 228
  if( note_number > 127 ) // just another check to ensure that no invalid note will be sent
229
    note_number = 127;
230
 
231
  // we have three transitions which are for interest:
232
  // a) first switch changes from 1->0 (pin_value == 0):
233
  //    - store the current timestamp
234
  // b) second switch changes from 1->0 (pin_value == 0):
235
  //    - calculate delay between current timestamp and timestamp captured during a)
236
  //    - do this only if the captured timestamp is != 0 (see also c)
237
  //    - calculate velocity depending on the delay
238
  //    - send Note On event
239
  // c) first switch changes from 0->1 (pin_value == 1): 
240
  //    - send Note Off event (resp. Note On with velocity 0)
241
  //    - clear captured timestamp (allows to check for valid delay on next transition)
242
 
1101 tk 243
  unsigned key_ix = pin & 0xfffffffe;
1092 tk 244
  int delay = -1;
1096 tk 245
  u8 send_note_on = 0;
246
  u8 send_note_off = 0;
247
 
248
  if( pin_value == 0 ) {
249
    if( second_switch == 0 ) { // first switch
1101 tk 250
      last_timestamp[key_ix] = timestamp;
1096 tk 251
    } else { // second switch
1101 tk 252
      if( last_timestamp[key_ix] ) {
253
    delay = timestamp - last_timestamp[key_ix];
1096 tk 254
    send_note_on = 1;
255
      }
256
    }
257
  } else {
258
    if( second_switch == 0 ) { // first switch
1101 tk 259
      last_timestamp[key_ix] = 0;
1102 tk 260
      send_note_off = 1;
1096 tk 261
    }
1092 tk 262
  }
263
 
1096 tk 264
 
265
  // now we know:
266
  // - if a note on or off event should be sent
267
  // - the measured delay (note on only)
268
 
269
  if( send_note_on ) {
270
    // determine velocity depending on delay
1109 tk 271
    int velocity = 127 - (((delay-keyboard_delay_fastest) * 127) / (keyboard_delay_slowest-keyboard_delay_fastest));
1096 tk 272
    // saturate to ensure that range 1..127 won't be exceeded
273
    if( velocity < 1 )
274
      velocity = 1;
275
    if( velocity > 127 )
276
      velocity = 127;
277
 
278
    MIOS32_MIDI_SendNoteOn(KEYBOARD_MIDI_PORT, KEYBOARD_MIDI_CHN, note_number, velocity);
1086 tk 279
#if DEBUG_VERBOSE_LEVEL >= 2
1101 tk 280
    DEBUG_MSG("row=0x%02x, column=0x%02x, pin_value=%d -> pin=%d, timestamp=%u -> NOTE ON (delay=%d); velocity=%d\n",
1103 tk 281
          row, column, pin_value, pin, timestamp, delay, velocity);
1086 tk 282
#endif
1096 tk 283
  } else if( send_note_off ) {
284
    // send Note On with velocity 0
285
    MIOS32_MIDI_SendNoteOn(KEYBOARD_MIDI_PORT, KEYBOARD_MIDI_CHN, note_number, 0x00);
1092 tk 286
 
1096 tk 287
#if DEBUG_VERBOSE_LEVEL >= 2
1101 tk 288
    DEBUG_MSG("row=0x%02x, column=0x%02x, pin_value=%d -> pin=%d, timestamp=%u -> NOTE OFF\n",
1103 tk 289
          row, column, pin_value, pin, timestamp);
1096 tk 290
#endif
291
  } else {
292
#if DEBUG_VERBOSE_LEVEL >= 2
1101 tk 293
    DEBUG_MSG("row=0x%02x, column=0x%02x, pin_value=%d -> pin=%d, timestamp=%u -> IGNORE\n",
1103 tk 294
          row, column, pin_value, pin, timestamp);
1096 tk 295
#endif
296
  }
1086 tk 297
}
298
 
1092 tk 299
 
1086 tk 300
static void TASK_MatrixScan(void *pvParameters)
301
{
302
  while( 1 ) {
303
    // wait for next timesplice (1 mS)
304
    vTaskDelay(1 / portTICK_RATE_MS);
305
 
1092 tk 306
    // determine timestamp (we need it for delay measurements)
307
    mios32_sys_time_t t = MIOS32_SYS_TimeGet();
308
    u32 timestamp = 1000*t.seconds + t.fraction_ms;
309
 
1108 tk 310
    // loop:
311
    //   - latch DIN/DOUT values
312
    //   - shift selection pattern for *next* row to DOUT registers
313
    //   - read DIN values of previously selected row
314
    // since we need to select the first row before the first DIN values are latched, we loop from -1
315
    // to handle the initial state
1086 tk 316
    int row;
1108 tk 317
    for(row=-1; row<MATRIX_NUM_ROWS; ++row) {
318
      if( row >= 0 ) { // not required for initial scan
1109 tk 319
    // latch DIN values
1108 tk 320
    MIOS32_SPI_RC_PinSet(MIOS32_SRIO_SPI, MIOS32_SRIO_SPI_RC_PIN, 0); // spi, rc_pin, pin_value
321
    MIOS32_DELAY_Wait_uS(1);
322
    MIOS32_SPI_RC_PinSet(MIOS32_SRIO_SPI, MIOS32_SRIO_SPI_RC_PIN, 1); // spi, rc_pin, pin_value
323
      }
1086 tk 324
 
325
      // determine selection mask for next row (written into DOUT registers while reading DIN registers)
326
      u16 select_row_pattern = ~(1 << (row+1));
327
#if MATRIX_DOUT_HAS_SINK_DRIVERS
328
      select_row_pattern ^= 0xffff; // invert selection pattern if sink drivers are connected to DOUT pins
329
#endif
330
 
331
      // read DIN, write DOUT
332
      u8 din0 = MIOS32_SPI_TransferByte(MIOS32_SRIO_SPI, (select_row_pattern >> 8) & 0xff);
333
      u8 din1 = MIOS32_SPI_TransferByte(MIOS32_SRIO_SPI, (select_row_pattern >> 0) & 0xff);
334
 
1109 tk 335
      // latch new DOUT value
336
      MIOS32_SPI_RC_PinSet(MIOS32_SRIO_SPI, MIOS32_SRIO_SPI_RC_PIN, 0); // spi, rc_pin, pin_value
337
      MIOS32_DELAY_Wait_uS(1);
338
      MIOS32_SPI_RC_PinSet(MIOS32_SRIO_SPI, MIOS32_SRIO_SPI_RC_PIN, 1); // spi, rc_pin, pin_value
339
 
340
      if( row >= 0 ) {
341
    // combine read DIN bytes to 16bit value
1108 tk 342
    u16 din_pattern = (din1 << 8) | din0;
1107 tk 343
 
1108 tk 344
    // check if values have been changed via XOR combination with previously scanned value
345
    u16 changed = din_pattern ^ din_value[row];
346
    if( changed ) {
347
      // store changed value
348
      din_value[row] = din_pattern;
1086 tk 349
 
1108 tk 350
      // notify changed value
351
      int column;
352
      for(column=0; column<16; ++column) {
353
        u16 mask = 1 << column;
354
        if( changed & mask )
355
          BUTTON_NotifyToggle(row, column, (din_pattern & mask) ? 1 : 0, timestamp);
356
      }
1086 tk 357
    }
358
      }
359
    }
360
  }
361
}