Subversion Repositories svn.mios32

Rev

Rev 1919 | Rev 2113 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1424 tk 1
// $Id: app.c 1920 2014-01-08 19:29:35Z tk $
2
/*
1426 tk 3
 * MIOS32 Tutorial #029: Fast Scan Matrix for velocity sensitive Keyboard
1424 tk 4
 * see README.txt for details
5
 *
6
 * ==========================================================================
7
 *
8
 *  Copyright (C) 2010 Thorsten Klose(tk@midibox.org)
9
 *  Licensed for personal non-commercial use only.
10
 *  All other rights reserved.
11
 *
12
 * ==========================================================================
13
 */
14
 
15
/////////////////////////////////////////////////////////////////////////////
16
// Include files
17
/////////////////////////////////////////////////////////////////////////////
18
 
19
#include <mios32.h>
20
#include "app.h"
21
 
22
#include <FreeRTOS.h>
23
#include <task.h>
24
#include <queue.h>
25
 
26
 
27
/////////////////////////////////////////////////////////////////////////////
28
// for optional debugging messages
29
/////////////////////////////////////////////////////////////////////////////
30
 
31
// level >= 1: print warnings (recommended default value)
32
// level >= 2: print debug messages to output velocity values
33
#define DEBUG_VERBOSE_LEVEL 2
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 columns for up to 8*8 keys (each key connected to two rows)
43
#define MATRIX_NUM_ROWS 16
44
 
45
// DOUT/DIN SRs (counted from 1, 0 disables SR)
46
#define MATRIX_DOUT_SR1 1
47
#define MATRIX_DOUT_SR2 2
48
#define MATRIX_DIN_SR   1
49
 
50
// maximum number of supported keys
51
#define KEYBOARD_NUM_PINS (8*16)
52
 
53
// used MIDI port and channel (DEFAULT, USB0, UART0 or UART1)
54
#define KEYBOARD_MIDI_PORT DEFAULT
55
#define KEYBOARD_MIDI_CHN  Chn1
56
 
57
// initial minimum/maximum delay to calculate velocity
58
// (will be copied into variables, so that values could be changed during runtime)
59
#define INITIAL_KEYBOARD_DELAY_FASTEST 4
60
#define INITIAL_KEYBOARD_DELAY_SLOWEST 200
61
 
62
 
63
/////////////////////////////////////////////////////////////////////////////
64
// Prototypes
65
/////////////////////////////////////////////////////////////////////////////
66
static void TASK_MatrixScan(void *pvParameters);
67
 
68
 
69
/////////////////////////////////////////////////////////////////////////////
70
// Local Variables
71
/////////////////////////////////////////////////////////////////////////////
72
 
73
static u8 selected_row;
74
static u8 din_value[MATRIX_NUM_ROWS];
75
static u8 din_value_changed[MATRIX_NUM_ROWS];
76
 
1430 tk 77
// for velocity
78
static u16 timestamp;
79
static u16 din_activated_timestamp[KEYBOARD_NUM_PINS];
1424 tk 80
 
1430 tk 81
// for debouncing each pin has a flag which notifies if the Note On/Off value has already been sent
82
#if (KEYBOARD_NUM_PINS % 8)
83
# error "KEYBOARD_NUM_PINS must be dividable by 8!"
84
#endif
85
static u8 din_note_on_sent[KEYBOARD_NUM_PINS / 8];
86
static u8 din_note_off_sent[KEYBOARD_NUM_PINS / 8];
87
 
88
// soft-configuration (could be changed during runtime)
1424 tk 89
static u32 keyboard_delay_fastest;
90
static u32 keyboard_delay_slowest;
91
 
92
/////////////////////////////////////////////////////////////////////////////
93
// This hook is called after startup to initialize the application
94
/////////////////////////////////////////////////////////////////////////////
95
void APP_Init(void)
96
{
97
  // initialize all LEDs
98
  MIOS32_BOARD_LED_Init(0xffffffff);
99
 
100
  // start with first column
101
  selected_row = 0;
102
 
103
  // initialize DIN arrays
104
  int row;
105
  for(row=0; row<MATRIX_NUM_ROWS; ++row) {
106
    din_value[row] = 0xff; // default state: buttons depressed
107
    din_value_changed[row] = 0x00;
108
  }
109
 
110
  // initialize timestamps
111
  int i;
112
  timestamp = 0;
113
  for(i=0; i<KEYBOARD_NUM_PINS; ++i) {
114
    din_activated_timestamp[i] = 0;
115
  }
116
 
1430 tk 117
  for(i=0; i<KEYBOARD_NUM_PINS/8; ++i) {
118
    din_note_on_sent[i] = 0x00;
119
    din_note_off_sent[i] = 0x00;
120
  }
121
 
1424 tk 122
  // initialize keyboard delay values
123
  keyboard_delay_fastest = INITIAL_KEYBOARD_DELAY_FASTEST;
124
  keyboard_delay_slowest = INITIAL_KEYBOARD_DELAY_SLOWEST;
125
 
126
  // start matrix scan task
127
  xTaskCreate(TASK_MatrixScan, (signed portCHAR *)"MatrixScan", configMINIMAL_STACK_SIZE, NULL, PRIORITY_TASK_MATRIX_SCAN, NULL);
128
 
129
  // limit the number of DIN/DOUT SRs which will be scanned for faster scan rate
130
  MIOS32_SRIO_ScanNumSet(2);
131
 
132
  // speed up SPI transfer rate (was MIOS32_SPI_PRESCALER_128, initialized by MIOS32_SRIO_Init())
133
  MIOS32_SPI_TransferModeInit(MIOS32_SRIO_SPI, MIOS32_SPI_MODE_CLK1_PHASE1, MIOS32_SPI_PRESCALER_64);
134
  // prescaler 64 results into a transfer rate of 0.64 uS per bit
135
  // when 2 SRs are transfered, we are able to scan the whole 16x8 matrix in 300 uS
136
 
137
  // standard SRIO scan has been disabled in programming_models/traditional/main.c via MIOS32_DONT_SERVICE_SRIO_SCAN in mios32_config.h
138
  // start the scan here - and retrigger it whenever it's finished
139
  APP_SRIO_ServicePrepare();
140
  MIOS32_SRIO_ScanStart(APP_SRIO_ServiceFinish);
141
}
142
 
143
 
144
/////////////////////////////////////////////////////////////////////////////
145
// This task is running endless in background
146
/////////////////////////////////////////////////////////////////////////////
147
void APP_Background(void)
148
{
149
  // init LCD
150
  MIOS32_LCD_Clear();
151
  MIOS32_LCD_CursorSet(0, 0);
152
  MIOS32_LCD_PrintString("see README.txt   ");
153
  MIOS32_LCD_CursorSet(0, 1);
154
  MIOS32_LCD_PrintString("for details     ");
155
 
156
  // endless loop
157
  while( 1 ) {
1919 tk 158
    // do nothing
1424 tk 159
  }
160
}
161
 
162
 
163
/////////////////////////////////////////////////////////////////////////////
1919 tk 164
// This hook is called each mS from the main task which also handles DIN, ENC
165
// and AIN events. You could add more jobs here, but they shouldn't consume
166
// more than 300 uS to ensure the responsiveness of buttons, encoders, pots.
167
// Alternatively you could create a dedicated task for application specific
168
// jobs as explained in $MIOS32_PATH/apps/tutorials/006_rtos_tasks
169
/////////////////////////////////////////////////////////////////////////////
170
void APP_Tick(void)
171
{
1920 tk 172
  // PWM modulate the status LED (this is a sign of life)
173
  u32 timestamp = MIOS32_TIMESTAMP_Get();
174
  MIOS32_BOARD_LED_Set(1, (timestamp % 20) <= ((timestamp / 100) % 10));
1919 tk 175
}
176
 
177
 
178
/////////////////////////////////////////////////////////////////////////////
179
// This hook is called each mS from the MIDI task which checks for incoming
180
// MIDI events. You could add more MIDI related jobs here, but they shouldn't
181
// consume more than 300 uS to ensure the responsiveness of incoming MIDI.
182
/////////////////////////////////////////////////////////////////////////////
183
void APP_MIDI_Tick(void)
184
{
185
}
186
 
187
 
188
/////////////////////////////////////////////////////////////////////////////
1424 tk 189
// This hook is called when a MIDI package has been received
190
/////////////////////////////////////////////////////////////////////////////
191
void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package)
192
{
193
}
194
 
195
 
196
/////////////////////////////////////////////////////////////////////////////
197
// This hook is called before the shift register chain is scanned
198
/////////////////////////////////////////////////////////////////////////////
199
void APP_SRIO_ServicePrepare(void)
200
{
201
  // select next column, wrap at 16
202
  if( ++selected_row >= MATRIX_NUM_ROWS ) {
203
    selected_row = 0;
204
 
205
    // increment timestamp for velocity delay measurements
206
    ++timestamp;
207
  }
208
 
209
  // selection pattern (active selection is 0, all other outputs 1)
210
  u16 selection_mask = ~(1 << (u16)selected_row);
211
 
212
  // transfer to DOUTs
213
#if MATRIX_DOUT_SR1
214
  MIOS32_DOUT_SRSet(MATRIX_DOUT_SR1-1, (selection_mask >> 0) & 0xff);
215
#endif
216
#if MATRIX_DOUT_SR2
217
  MIOS32_DOUT_SRSet(MATRIX_DOUT_SR2-1, (selection_mask >> 8) & 0xff);
218
#endif
219
}
220
 
221
 
222
/////////////////////////////////////////////////////////////////////////////
223
// This hook is called after the shift register chain has been scanned
224
/////////////////////////////////////////////////////////////////////////////
225
void APP_SRIO_ServiceFinish(void)
226
{
227
  // check DINs
228
#if MATRIX_DIN_SR
1471 tk 229
  // the DIN scan was done with previous row selection, not the current one:
1473 tk 230
  int prev_row = selected_row ? (selected_row - 1) : (MATRIX_NUM_ROWS - 1);
1471 tk 231
 
1424 tk 232
  u8 sr = MATRIX_DIN_SR;
233
  MIOS32_DIN_SRChangedGetAndClear(sr-1, 0xff); // ensure that change won't be propagated to normal DIN handler
234
  u8 sr_value = MIOS32_DIN_SRGet(sr-1);
235
 
236
  // determine pin changes
1471 tk 237
  u8 changed = sr_value ^ din_value[prev_row];
1424 tk 238
 
239
  if( changed ) {
240
    // add them to existing notifications
1471 tk 241
    din_value_changed[prev_row] |= changed;
1424 tk 242
 
243
    // store new value
1471 tk 244
    din_value[prev_row] = sr_value;
1424 tk 245
 
246
    // store timestamp for changed pin on 1->0 transition
247
    u8 sr_pin;
248
    u8 mask = 0x01;
249
    for(sr_pin=0; sr_pin<8; ++sr_pin, mask <<= 1) {
250
      if( (changed & mask) && !(sr_value & mask) ) {
1471 tk 251
    din_activated_timestamp[prev_row*8 + sr_pin] = timestamp;
1424 tk 252
      }
253
    }
254
  }
255
 
256
#endif
257
 
258
  // standard SRIO scan has been disabled in programming_models/traditional/main.c via MIOS32_DONT_SERVICE_SRIO_SCAN in mios32_config.h
259
  // start the scan here - and retrigger it whenever it's finished
260
  APP_SRIO_ServicePrepare();
261
  MIOS32_SRIO_ScanStart(APP_SRIO_ServiceFinish);
262
}
263
 
264
 
265
/////////////////////////////////////////////////////////////////////////////
266
// This hook is called when a button has been toggled
267
// pin_value is 1 when button released, and 0 when button pressed
268
/////////////////////////////////////////////////////////////////////////////
269
void APP_DIN_NotifyToggle(u32 pin, u32 pin_value)
270
{
271
}
272
 
273
 
274
/////////////////////////////////////////////////////////////////////////////
275
// This hook is called when an encoder has been moved
276
// incrementer is positive when encoder has been turned clockwise, else
277
// it is negative
278
/////////////////////////////////////////////////////////////////////////////
279
void APP_ENC_NotifyChange(u32 encoder, s32 incrementer)
280
{
281
}
282
 
283
 
284
/////////////////////////////////////////////////////////////////////////////
285
// This hook is called when a pot has been moved
286
/////////////////////////////////////////////////////////////////////////////
287
void APP_AIN_NotifyChange(u32 pin, u32 pin_value)
288
{
289
}
290
 
291
 
292
 
293
/////////////////////////////////////////////////////////////////////////////
294
// This task is called each mS to scan the button matrix
295
/////////////////////////////////////////////////////////////////////////////
296
 
297
// will be called on button pin changes (see TASK_BLM_Check)
1430 tk 298
void BUTTON_NotifyToggle(u8 row, u8 column, u8 depressed)
1424 tk 299
{
300
  // determine pin number based on row/column
301
 
1478 tk 302
  // each key has two contacts, I call them "break contact" and "make contact"
1424 tk 303
  // the assignments can be determined by setting DEBUG_VERBOSE_LEVEL to 2
1430 tk 304
 
305
  // default: linear addressing (e.g. Fatar Keyboards?)
1478 tk 306
  // the make contacts are at row 0, 2, 4, 6, 8, 10, 12, 14
307
  // the break contacts are at row 1, 3, 5, 7, 9, 11, 13, 15
1430 tk 308
 
309
  // determine key number:
310
  int key = 8*(row / 2) + column;
311
 
1478 tk 312
  // check if key is assigned to an "break contact"
313
  u8 break_contact = (row & 1); // odd numbers
1430 tk 314
 
315
  // determine note number (here we could insert an octave shift)
316
  int note_number = key + 36;
1424 tk 317
 
1478 tk 318
  // reference to break and make pin
319
  int pin_make = (row)*8 + column;
320
  int pin_break = (row+1)*8 + column;
1471 tk 321
 
1430 tk 322
  // ensure valid note range
323
  if( note_number > 127 )
324
    note_number = 127;
325
  else if( note_number < 0 )
326
    note_number = 0;
327
 
328
#if DEBUG_VERBOSE_LEVEL >= 2
1478 tk 329
  DEBUG_MSG("row=%d, column=%d, depressed=%d  -->  key=%d, break_contact:%d, note_number=%d\n",
330
        row, column, depressed, key, break_contact, note_number);
1430 tk 331
#endif
332
 
333
  // determine key mask and pointers for access to combined arrays
334
  u8 key_mask = (1 << (key % 8));
335
  u8 *note_on_sent = (u8 *)&din_note_on_sent[key / 8];
336
  u8 *note_off_sent = (u8 *)&din_note_off_sent[key / 8];
337
 
338
 
1478 tk 339
  // break contacts don't send MIDI notes, but they are used for delay measurements,
1430 tk 340
  // and they release the Note On/Off debouncing mechanism
1478 tk 341
  if( break_contact ) {
1430 tk 342
    if( depressed ) {
343
      *note_on_sent &= ~key_mask;
344
      *note_off_sent &= ~key_mask;
345
    }
346
    return;
347
  }
348
 
1424 tk 349
  // branch depending on pressed or released key
1430 tk 350
  if( depressed ) {
351
    if( !(*note_off_sent & key_mask) ) {
352
      *note_off_sent |= key_mask;
353
 
1424 tk 354
#if DEBUG_VERBOSE_LEVEL >= 2
1430 tk 355
      DEBUG_MSG("DEPRESSED key=%d\n", key);
1424 tk 356
#endif
357
 
1430 tk 358
      MIOS32_MIDI_SendNoteOn(KEYBOARD_MIDI_PORT, KEYBOARD_MIDI_CHN, note_number, 0x00); // velocity 0
359
    }
1424 tk 360
 
361
  } else {
362
 
1430 tk 363
    if( !(*note_on_sent & key_mask) ) {
364
      *note_on_sent |= key_mask;
1424 tk 365
 
1478 tk 366
      // determine timestamps between break and make contact
367
      u16 timestamp_break = din_activated_timestamp[pin_break];
368
      u16 timestamp_make = din_activated_timestamp[pin_make];
1430 tk 369
      // and the delta delay (IMPORTANT: delay variable needs same resolution like timestamps to handle overrun correctly!)
1478 tk 370
      s16 delay = timestamp_make - timestamp_break;
1430 tk 371
 
372
      int velocity = 127;
373
      if( delay > keyboard_delay_fastest ) {
374
    // determine velocity depending on delay
375
    // lineary scaling - here we could also apply a curve table
376
    velocity = 127 - (((delay-keyboard_delay_fastest) * 127) / (keyboard_delay_slowest-keyboard_delay_fastest));
377
    // saturate to ensure that range 1..127 won't be exceeded
378
    if( velocity < 1 )
379
      velocity = 1;
380
    if( velocity > 127 )
381
      velocity = 127;
382
      }
383
 
1424 tk 384
#if DEBUG_VERBOSE_LEVEL >= 2
1430 tk 385
      DEBUG_MSG("PRESSED key=%d, delay=%d, velocity=%d\n", key, delay, velocity);
1424 tk 386
#endif
387
 
1430 tk 388
      MIOS32_MIDI_SendNoteOn(KEYBOARD_MIDI_PORT, KEYBOARD_MIDI_CHN, note_number, velocity);
389
    }
1424 tk 390
  }
391
}
392
 
393
 
394
static void TASK_MatrixScan(void *pvParameters)
395
{
396
  while( 1 ) {
397
    // wait for next timesplice (1 mS)
398
    vTaskDelay(1 / portTICK_RATE_MS);
399
 
400
    // check for DIN pin changes
401
    int row;
402
    for(row=0; row<MATRIX_NUM_ROWS; ++row) {
403
        // check if there are pin changes - must be atomic!
404
        MIOS32_IRQ_Disable();
405
        u8 changed = din_value_changed[row];
406
        din_value_changed[row] = 0;
407
        MIOS32_IRQ_Enable();
408
 
409
        // any pin change at this SR?
410
        if( !changed )
411
          continue;
412
 
413
        // check all 8 pins of the SR
414
        int sr_pin;
415
        u8 mask = 0x01;
416
        for(sr_pin=0; sr_pin<8; ++sr_pin, mask <<= 1)
417
          if( changed & mask )
418
            BUTTON_NotifyToggle(row, sr_pin, (din_value[row] & mask) ? 1 : 0);
419
    }
420
  }
421
}