summaryrefslogtreecommitdiff
path: root/arduino/Laserharfe/Laserharfe.ino
blob: a0e8d0717553d194b5d7e6d86fa45ab601981b50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/*
 * MIDIUSB_test.ino
 *
 * Created: 4/6/2015 10:47:08 AM
 * Author: gurbrinder grewal
 * Modified by Arduino LLC (2015)
 */ 

#include "MIDIUSB.h"
#include <SD.h>
#include <Wire.h>

#include "main.h"
#include "config.h"
#include "engine.h"

int led = 13;

enum {
  MODE_STANDALONE = 0,
  MODE_REPORTPOINTS = 1,
  MODE_REPORTPOINTS_PLAY = 2,
} g_mode = MODE_REPORTPOINTS_PLAY;

uint32_t now() {
  return millis();
}

void setup_pwm() {
  // Set up the generic clock (GCLK4) used to clock timers
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 2: 48MHz/2=24MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Feed GCLK4 to TC4 and TC5
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TC4 and TC5
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_TC4_CTRLA |= TC_CTRLA_MODE_COUNT8;          // Set the counter to 8-bit mode
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization 

  REG_TC4_COUNT8_CC0 = 0;                         // Set the TC4 CC0 register to some arbitary value
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
  REG_TC4_COUNT8_CC1 = 0;                         // Set the TC4 CC1 register to some arbitary value
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
  REG_TC4_COUNT8_PER = 1;                         // Set the PER (period) register to flip each time
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization

  NVIC_DisableIRQ(TC4_IRQn);
  NVIC_ClearPendingIRQ(TC4_IRQn);
  // NVIC_SetPriority(TC4_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
  // NVIC_EnableIRQ(TC4_IRQn);         // Connect TC4 to Nested Vector Interrupt Controller (NVIC) 

  REG_TC4_INTFLAG |= TC_INTFLAG_MC1 | TC_INTFLAG_MC0 | TC_INTFLAG_OVF;        // Clear the interrupt flags
  //REG_TC4_INTENSET = TC_INTENSET_MC1 | TC_INTENSET_MC0 | TC_INTENSET_OVF;    // Enable TC4 interrupts
  REG_TC4_INTENCLR = TC_INTENCLR_MC1 | TC_INTENCLR_MC0 | TC_INTENCLR_OVF;     // Disable TC4 interrupts

  int outpin = 15;

  // Enable the port multiplexer for the digital pin D0 
  PORT->Group[g_APinDescription[outpin].ulPort].PINCFG[g_APinDescription[outpin].ulPin].bit.PMUXEN = 1;

  // Connect the TC4 timer to the port output D0 - port pins are paired odd PMUXO and even PMUXE
  // Peripheral E specifies the TC timers, on this pin: TC4
  PORT->Group[g_APinDescription[outpin].ulPort].PMUX[g_APinDescription[outpin].ulPin >> 1].reg |= /*PORT_PMUX_PMUXO_E |*/ PORT_PMUX_PMUXE_E;

  REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1  |     // Set prescaler to 1, 24MHz/1 = 24MHz
                   TC_CTRLA_ENABLE;               // Enable TC4
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
}

static void set_cam_register(uint8_t r, uint8_t v) {
  Wire.beginTransmission(0x58);
  Wire.write(r);
  Wire.write(v);
  Wire.endTransmission();
}

static void setup_cam() {
  set_cam_register(0x30, 0x01);
  set_cam_register(0x06, 0x90);
  set_cam_register(0x08, 0xc0);
  set_cam_register(0x1a, 0x40);
  set_cam_register(0x33, 0x01);
  set_cam_register(0x30, 0x08);
}

void read_config() {
  config_reset();
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }

  File configfile = SD.open("laserhar.cfg", FILE_READ);
  if (!configfile) {
    Serial.println("opening config failed");
    return;
  }

  char command[256];
  int command_len;
  int skip_command;

  while (configfile.available()) {
    char c = command[command_len++] = configfile.read();
    if (c=='\n') {
        if (!skip_command) {
            command[command_len] = 0;
            config_handle_line(command);
        }
        skip_command = 0;
        command_len = 0;
    }
    if (command_len==sizeof(command)) {
      // If we end up at end of buffer, ignore everying to next \n
      skip_command = 1;
      command_len = 0;
    }
  }
  configfile.close();
}

void setup() {
  pinMode(led, OUTPUT);

  Wire.begin();
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  setup_pwm();

  // Let PWM settle for a bit
  delay(100);
  setup_cam();

  Serial.begin(115200);
  read_config();
  digitalWrite(led, LOW);   // turn the LED on (HIGH is the voltage level)
}

void handle_wire() {
  uint8_t ic2_result[32];
  char text[64];

  Wire.beginTransmission(0x58);
  Wire.write(0x36);
  Wire.endTransmission();
  Wire.requestFrom(0x58, 11);

  for (int i=0; Wire.available() && i<=11; ++i)
    ic2_result[i] = Wire.read();

  LPoint p1, p2, p3, p4;

  int Ix1,Iy1,Ix2,Iy2;
  int Ix3,Iy3,Ix4,Iy4;
  int s;

  s   = ic2_result[3];
  p1.x = ((int)ic2_result[1]) | ( ( s << 4) & 0x300);
  p1.y = ((int)ic2_result[2]) | ( ( s << 2) & 0x300);
  p2.x = ((int)ic2_result[4]) | ( ( s << 8) & 0x300);
  p2.y = ((int)ic2_result[5]) | ( ( s << 6) & 0x300);

  s   = ic2_result[8];
  p3.x = ((int)ic2_result[6]) | ( ( s << 4) & 0x300);
  p3.y = ((int)ic2_result[7]) | ( ( s << 2) & 0x300);
  p4.x = ((int)ic2_result[9]) | ( ( s << 8) & 0x300);
  p4.y = ((int)ic2_result[10])| ( ( s << 6) & 0x300);

  if (p1.x==1023 && p2.x==1023 && p3.x==1023 && p4.x==1023)
    return;

  uint32_t now = millis();
  if (g_mode==MODE_STANDALONE || g_mode==MODE_REPORTPOINTS_PLAY) {
    if (p1.x!=1023)
      engine_handle_point(&p1, now);
    if (p2.x!=1023)
      engine_handle_point(&p2, now);
    if (p3.x!=1023)
      engine_handle_point(&p3, now);
    if (p4.x!=1023)
      engine_handle_point(&p4, now);
  }

  if (g_mode==MODE_STANDALONE)
    return;
  if (p1.x!=1023)
    Serial.write(text, sprintf(text, "%04d:%04d ", p1.x, p1.y));
  if (p2.x!=1023)
    Serial.write(text, sprintf(text, "%04d:%04d ", p2.x, p2.y));
  if (p3.x!=1023)
    Serial.write(text, sprintf(text, "%04d:%04d ", p3.x, p3.y));
  if (p4.x!=1023)
    Serial.write(text, sprintf(text, "%04d:%04d ", p4.x, p4.y));
  Serial.println("");
}

/* Do a fast nibble to hex representation conversion */
static unsigned char fromhex(unsigned char x) {
  x-='0'; if( x<=9) return x;
  x&=~0x20; x-='A'-'0';
  if( x<6 ) return x+10;
  return 0xff;
}

void handle_midi(char *command) {
//  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  uint8_t m[6];
  /* expect 6 hex encoded MIDI bytes and a newline */
  for (int i=0; i<6; ++i)
    if ((m[i] = fromhex(command[i])) == 0xff)
        return;
  if (command[6] != '\n')
    return;

  midiEventPacket_t p = { m[0], (m[0]<<4) | m[1], (m[2]<<4) | m[3], (m[4]<<4) | m[5] };
  MidiUSB.sendMIDI(p);
  // MidiUSB.flush();
}

void midi_playnote(int channel, int note, int octave_offset ) {
  midi_pitchbend(channel, 0);
  midiEventPacket_t p = { 0x9, 0x90 | channel, note + 12 * octave_offset, 0x7f };
  MidiUSB.sendMIDI(p);
}

void midi_silencenote( int channel, int note, int octave_offset ) {
  midiEventPacket_t p = { 0x8, 0x80 | channel, note + 12 * octave_offset, 0x7f };
  MidiUSB.sendMIDI(p);
}

void midi_pitchbend( int channel, int pitch ) {
  pitch += 8192;
  if (pitch < 0) pitch = 0;
  if (pitch > 16383) pitch = 16383;

  midiEventPacket_t p = { 0xe, 0xe0 | channel, 0x7f & pitch, 0x7f & (pitch>>7) };
  MidiUSB.sendMIDI(p);
}

void handle_configure(char *command) {

}

void handle_command(char *command) {
  switch (*command) {
    case 'M': /* Getting MIDI instruction */
        handle_midi(command+1);
        break;
    case 'S': /* Getting set mode instruction */
        switch (command[1]) {
          case '1':
            g_mode = MODE_STANDALONE;
            break;
          case '2':
            g_mode = MODE_REPORTPOINTS_PLAY;
            break;
          case '3':
            g_mode = MODE_REPORTPOINTS;
            break;
          default:
            break;
        }
        break;
    case 'C': /* Getting configure command */
        handle_configure(command+1);
        break;
    default:
        break;
  }
}

void handle_serial() {
  static char command[128];
  static int command_len;
  static int skip_command;

  while (Serial.available()) {
    char c = command[command_len++] = Serial.read();
    if (c=='\n') {
        if (!skip_command) {
            command[command_len] = 0;
            handle_command(command);
        }
        skip_command = 0;
        command_len = 0;
        // Read at max one command to allow polling the cam
        return;
    }
    if (command_len==sizeof(command)) {
      // If we end up at end of buffer, ignore everying to next \n
      skip_command = 1;
      command_len = 0;
    }
  }
}

void loop() {
  static int led_state;

  handle_wire();
  handle_serial();

/*
  Serial.write((const uint8_t*)"Sending note on\r\n", 17);
  noteOn(0, note, 64);   // Channel 0, middle C, normal velocity
  MidiUSB.flush();
  delay(500);
  digitalWrite(led, LOW);   // turn the LED on (HIGH is the voltage level)

  Serial.write((const uint8_t*)"Sending note off\r\n", 18);
  noteOff(0, note, 64);  // Channel 0, middle C, normal velocity
  MidiUSB.flush();
  delay(1500);
*/

  engine_checksilence(millis());
  digitalWrite(led, ++led_state & 1 ? HIGH : LOW);   // turn the LED on (HIGH is the voltage level)
}