Keyboard.c 28.3 KB
Newer Older
mntmn's avatar
mntmn committed
1
/*
mntmn's avatar
mntmn committed
2
3
4
  MNT Reform 2.0 Keyboard Firmware
  Copyright 2019-2021  Lukas F. Hartmann / MNT Research GmbH, Berlin
  lukas@mntre.com
mntmn's avatar
mntmn committed
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
*/
/*
             LUFA Library
     Copyright (C) Dean Camera, 2018.

  dean [at] fourwalledcubicle [dot] com
           www.lufa-lib.org
*/
/*
  Copyright 2018  Dean Camera (dean [at] fourwalledcubicle [dot] com)

  Permission to use, copy, modify, distribute, and sell this
  software and its documentation for any purpose is hereby granted
  without fee, provided that the above copyright notice appear in
  all copies and that both that the copyright notice and this
  permission notice and warranty disclaimer appear in supporting
  documentation, and that the name of the author not be used in
  advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.

  The author disclaims all warranties with regard to this
  software, including all implied warranties of merchantability
  and fitness.  In no event shall the author be liable for any
  special, indirect or consequential damages or any damages
  whatsoever resulting from loss of use, data or profits, whether
  in an action of contract, negligence or other tortious action,
  arising out of or in connection with the use or performance of
  this software.
*/

#include "Config/LUFAConfig.h"
#include "Keyboard.h"
#include <avr/io.h>
38
#include "LUFA/Drivers/Peripheral/Serial.h"
mntmn's avatar
mntmn committed
39
40
#include "ssd1306.h"
#include "scancodes.h"
41
#include <stdlib.h>
42
#include <avr/sleep.h>
mntmn's avatar
mntmn committed
43

44
#define KBD_FW_REV "R1 20210927"
mntmn's avatar
mntmn committed
45
//#define KBD_VARIANT_STANDALONE
46
#define KBD_VARIANT_QWERTY_US
mntmn's avatar
mntmn committed
47
//#define KBD_VARIANT_NEO2
48

49
#define COLS 14
50
51
#define ROWS 6

52
53
54
55
56
57
58
59
60
61
62
63
64
65
uint8_t matrix[COLS*6+2] = {
  KEY_ESCAPE, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, HID_KEYBOARD_SC_EXSEL,

  KEY_GRAVE_ACCENT_AND_TILDE, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS_AND_UNDERSCORE, KEY_EQUAL_AND_PLUS, KEY_BACKSPACE,

  KEY_TAB, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_OPENING_BRACKET_AND_OPENING_BRACE, KEY_CLOSING_BRACKET_AND_CLOSING_BRACE, KEY_BACKSLASH_AND_PIPE,

  HID_KEYBOARD_SC_LEFT_CONTROL, HID_KEYBOARD_SC_APPLICATION, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON_AND_COLON, KEY_APOSTROPHE_AND_QUOTE, KEY_ENTER,

  HID_KEYBOARD_SC_LEFT_SHIFT, HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN, HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN, KEY_SLASH_AND_QUESTION_MARK,  HID_KEYBOARD_SC_UP_ARROW, HID_KEYBOARD_SC_RIGHT_SHIFT,

  HID_KEYBOARD_SC_RIGHT_GUI, HID_KEYBOARD_SC_LEFT_GUI, HID_KEYBOARD_SC_RIGHT_CONTROL, KEY_SPACE, HID_KEYBOARD_SC_LEFT_ALT, HID_KEYBOARD_SC_RIGHT_ALT, KEY_SPACE, HID_KEYBOARD_SC_PAGE_UP, HID_KEYBOARD_SC_PAGE_DOWN, HID_KEYBOARD_SC_LEFT_ARROW, HID_KEYBOARD_SC_DOWN_ARROW, HID_KEYBOARD_SC_RIGHT_ARROW,  0xfe,0xed,0xca,0xfe
};

mntmn's avatar
mntmn committed
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
/** Buffer to hold the previously generated Keyboard HID report, for comparison purposes inside the HID class driver. */
static uint8_t PrevKeyboardHIDReportBuffer[sizeof(USB_KeyboardReport_Data_t)];

/** LUFA HID Class driver interface configuration and state information. This structure is
 *  passed to all HID Class driver functions, so that multiple instances of the same class
 *  within a device can be differentiated from one another.
 */
USB_ClassInfo_HID_Device_t Keyboard_HID_Interface =
  {
    .Config =
      {
        .InterfaceNumber              = INTERFACE_ID_Keyboard,
        .ReportINEndpoint             =
          {
            .Address              = KEYBOARD_EPADDR,
            .Size                 = KEYBOARD_EPSIZE,
            .Banks                = 1,
          },
        .PrevReportINBuffer           = PrevKeyboardHIDReportBuffer,
        .PrevReportINBufferSize       = sizeof(PrevKeyboardHIDReportBuffer),
      },
  };


#define output_low(port,pin) port &= ~(1<<pin)
#define output_high(port,pin) port |= (1<<pin)
#define set_input(portdir,pin) portdir &= ~(1<<pin)
#define set_output(portdir,pin) portdir |= (1<<pin)

95
96
uint8_t matrix_debounce[COLS*6];
uint8_t matrix_state[COLS*6];
97
uint8_t remote_som_power_expected_state = 0;
98

mntmn's avatar
mntmn committed
99
100
101
102
103
104
105
// f8 = sleep
// 49 = mute
// 84 = scroll lock

char r_inbuf[10];

void gfx_clear(void) {
106
107
108
109
110
  for (int y=0; y<4; y++) {
    for (int x=0; x<21; x++) {
      gfx_poke(x,y,' ');
    }
  }
111
  gfx_clear_invert();
mntmn's avatar
mntmn committed
112
113
114
}

void empty_serial(void) {
115
  int clock = 0;
116
  while (Serial_ReceiveByte()>=0 && clock<100) {
117
118
119
    // flush serial
    clock++;
  }
mntmn's avatar
mntmn committed
120
121
122
123
124
}

int term_x = 0;
int term_y = 0;

125
126
char response[64];

mntmn's avatar
mntmn committed
127
int remote_receive_string(int print) {
128
129
  char done = 0;
  int32_t clock = 0;
130
131
  int res_x = 0;
  response[0] = 0;
mntmn's avatar
mntmn committed
132

133
134
135
136
  while (!done) {
    int16_t chr = -1;
    clock = 0;
    while (chr==-1 || chr==0) {
137
      chr=Serial_ReceiveByte();
138
      clock++;
139
      if (clock>500000) goto timeout;
140
    }
mntmn's avatar
mntmn committed
141
    int poke_chr = chr;
142
143
    if (chr=='\n') poke_chr=' ';
    if (chr!='\r') {
mntmn's avatar
mntmn committed
144
145
146
147
148
149
150
151
152
153
      if (print) {
        gfx_poke(term_x,term_y,poke_chr);
        gfx_poke(term_x+1,term_y,' ');
        term_x++;
        if (term_x>=20) {
          term_x=0;
          term_y++;
          if (term_y>=3) {
            term_y=0;
          }
154
155
        }
      }
156
157
158
159
      if (res_x<63) {
        response[res_x++] = chr;
        response[res_x] = 0;
      }
160
    }
161
162
    if (chr=='\r') done = 1;
  }
mntmn's avatar
mntmn committed
163
timeout:
mntmn's avatar
mntmn committed
164
  if (!done && print) gfx_poke(20,0,'T');
165
  empty_serial();
mntmn's avatar
mntmn committed
166
  if (print) {
167
    gfx_flush();
mntmn's avatar
mntmn committed
168
169
  }
  return done;
170
171
172
173
}

void anim_hello(void) {
  gfx_clear();
174
  gfx_on();
175
176
177
  for (int y=0; y<3; y++) {
    for (int x=0; x<12; x++) {
      gfx_poke(x+4,y+1,(5+y)*32+x);
178
      gfx_flush();
179
180
181
    }
  }
  for (int y=0; y<0xff; y++) {
182
    gfx_contrast(y);
183
184
185
    Delay_MS(2);
  }
  for (int y=0; y<0xff; y++) {
186
    gfx_contrast(0xff-y);
187
188
189
190
191
    Delay_MS(2);
  }
}

void anim_goodbye(void) {
192
  gfx_clear();
193
  gfx_on();
194
195
196
197
198
199
200
201
  for (int y=0; y<3; y++) {
    for (int x=0; x<12; x++) {
      gfx_poke(x+4,y+1,(5+y)*32+x);
    }
  }
  for (int y=0; y<3; y++) {
    for (int x=0; x<12; x++) {
      gfx_poke(x+4,y+1,' ');
202
      gfx_flush();
203
204
    }
  }
205
  gfx_off();
mntmn's avatar
mntmn committed
206
207
}

208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
float voltages[8];

void insert_bat_icon(char* str, int x, float v) {
  char icon = 0;
  if (v>=3.3) {
    icon = 8;
  } else if (v>=3.1) {
    icon = 6;
  } else if (v>=3.0) {
    icon = 4;
  } else if (v>=2.9) {
    icon = 2;
  } else {
    icon = 0;
  }
  str[x]   = 4*32+icon;
  str[x+1] = 4*32+icon+1;
mntmn's avatar
mntmn committed
225
226
}

227
void remote_try_wakeup(void) {
228
  char buf[64];
229

230
231
232
233
234
235
236
  for (int i=0; i<1000; i++) {
    if (i%10 == 0) {
      gfx_clear();
      sprintf(buf, "Waking up LPC... %d%%", i/4);
      gfx_poke_str(0, 0, buf);
      gfx_flush();
    }
mntmn's avatar
mntmn committed
237

238
239
    Serial_SendByte('a');
    Serial_SendByte('\r');
240

241
242
243
244
    if (Serial_ReceiveByte()>0) {
      remote_receive_string(0);
      break;
    }
mntmn's avatar
mntmn committed
245

246
247
    Delay_MS(25);
  }
248
  Serial_SendByte('\r');
249
250
251
252
253
254
255
256
  Delay_MS(10);
  while (remote_receive_string(0)) {
    Delay_MS(25);
  }
}

int remote_try_command(char* cmd, int print_response) {
  int ok = 0;
257

258
259
260
261
262
  empty_serial();
  for (int tries=0; tries<2; tries++) {
    for (int i=0; i<strlen(cmd); i++) {
      Serial_SendByte(cmd[i]);
    }
263
264
    Serial_SendByte('\r');
    Delay_MS(1);
265
266
267
268
269
270
271
272
273
274
275
276

    if (print_response) {
      term_x = 0;
      term_y = 0;
    }
    ok = remote_receive_string(print_response);

    if (!ok && tries == 0) {
      remote_try_wakeup();
      empty_serial();
    }
    if (ok) break;
277
278
279
280
281
282
  }
  if (!ok) {
    gfx_clear();
    gfx_poke_str(0, 0, "No response from LPC.");
    gfx_flush();
  }
mntmn's avatar
mntmn committed
283

284
285
286
287
288
289
290
291
292
293
294
295
296
297
  empty_serial();
  return ok;
}

void remote_get_voltages(void) {
  term_x = 0;
  term_y = 0;

  float bat_volts = 0;
  float bat_amps = 0;
  char bat_gauge[5] = {0,0,0,0,0};

  int ok = remote_try_command("c", 0);
  if (!ok) return;
298
299
300
301
302

  // lpc format: 32 32 32 32 32 32 32 32 mA 0256mV26143 ???% P1
  //             |  |  |  |  |  |  |  |  | |      |     |    |
  //             0  3  6  9  12 15 18 21 24|      |     |    |
  //                                       26     33    39   44
303
304
  //                                       |
  //                                       `- can be a minus
305
306
  float sum_volts = 0;

307
  for (int i=0; i<8; i++) {
308
    voltages[i] = ((float)((response[i*3]-'0')*10 + (response[i*3+1]-'0')))/10.0;
309
310
    if (voltages[i]<0) voltages[i]=0;
    if (voltages[i]>=10) voltages[i]=9.9;
311
    sum_volts += voltages[i];
312
  }
313

314
  int amps_offset = 3*8+2;
315
  // cut off string
316
  response[amps_offset+5]=0;
317
  bat_amps = ((float)atoi(&response[amps_offset]))/1000.0;
318
  int volts_offset = amps_offset+5+2;
319
320
321
322
  response[volts_offset+5]=0;
  bat_volts = ((float)atoi(&response[volts_offset]))/1000.0;
  int gauge_offset = volts_offset+5+1;
  strncpy(bat_gauge, &response[gauge_offset], 4);
323

324
  char* power_str = "   ";
325
  int syspower_offset = gauge_offset+5;
326
327
328
329
330
331
  char power_digit = response[syspower_offset+1];
  if (power_digit == '1') {
    power_str = " On";
  } else if (power_digit == '0') {
    power_str = "Off";
  }
332

333
  // plot
mntmn's avatar
mntmn committed
334
  gfx_clear();
335
  char str[32];
mntmn's avatar
mntmn committed
336

337
  sprintf(str,"[] %.1f  [] %.1f   %s",voltages[0],voltages[4],bat_gauge);
338
339
340
  insert_bat_icon(str,0,voltages[0]);
  insert_bat_icon(str,8,voltages[4]);
  gfx_poke_str(0,0,str);
mntmn's avatar
mntmn committed
341

342
  sprintf(str,"[] %.1f  [] %.1f    %s",voltages[1],voltages[5],power_str);
343
344
345
  insert_bat_icon(str,0,voltages[1]);
  insert_bat_icon(str,8,voltages[5]);
  gfx_poke_str(0,1,str);
mntmn's avatar
mntmn committed
346

347
348
349
350
351
  if (bat_amps>=0) {
    sprintf(str,"[] %.1f  [] %.1f %2.3fA",voltages[2],voltages[6],bat_amps);
  } else {
    sprintf(str,"[] %.1f  [] %.1f %2.2fA",voltages[2],voltages[6],bat_amps);
  }
352
353
354
  insert_bat_icon(str,0,voltages[2]);
  insert_bat_icon(str,8,voltages[6]);
  gfx_poke_str(0,2,str);
mntmn's avatar
mntmn committed
355

mntmn's avatar
mntmn committed
356
  sprintf(str,"[] %.1f  [] %.1f %2.2fV",voltages[3],voltages[7],bat_volts);
357
358
359
  insert_bat_icon(str,0,voltages[3]);
  insert_bat_icon(str,8,voltages[7]);
  gfx_poke_str(0,3,str);
360
  gfx_flush();
mntmn's avatar
mntmn committed
361
362
}

363
364
365
366
int low_battery_alert = 0;

void remote_check_for_low_battery(void) {
  char bat_gauge[5] = {0,0,0,0,0};
367

368
369
370
371
372
373
  low_battery_alert = 0;
  empty_serial();

  Serial_SendByte('c');
  Serial_SendByte('\r');
  Delay_MS(1);
374
375
  int ok = remote_receive_string(0);
  if (!ok) return;
376
377

  for (int i=0; i<8; i++) {
378
    // TODO: only accept digits
379
380
381
382
383
384
385
386
    voltages[i] = ((float)((response[i*3]-'0')*10 + (response[i*3+1]-'0')))/10.0;
    if (voltages[i]<0) voltages[i]=0;
    if (voltages[i]>=10) voltages[i]=9.9;
    if (voltages[i]<3.0) {
      low_battery_alert = 1;
    }
  }

387
388
  int gauge_offset = 3*8+2+5+2+5+1;
  strncpy(bat_gauge, &response[gauge_offset], 3);
389

390
391
392
393
394
395
396
397
  if (bat_gauge[0] == '?') {
    // battery charge level unknown
  } else {
    int percent = atoi(bat_gauge);
    if (percent<10) {
      low_battery_alert = 1;
    }
  }
398
399
400
401
402
403
404
405
406
407
408
409
410
411

  int syspower_offset = gauge_offset+5;
  if (response[syspower_offset] == 'P') {
    char digit = response[syspower_offset+1];
    if (digit == '0' || digit == '1') {
      int is_computer_on = (digit == '1');
      if (!is_computer_on && remote_som_power_expected_state == 1) {
        // LPC says the computer is off, but we didn't expect it to be.
        // the only way this happens is if LPC turned off the system
        // due to a low battery condition.
        //
        // The keyboard will then go to sleep accordingly.

        EnterPowerOff();
mntmn's avatar
mntmn committed
412
        reset_keyboard_state();
413
414
415
416
      }
      remote_som_power_expected_state = is_computer_on;
    }
  }
417
418
}

mntmn's avatar
mntmn committed
419
void remote_get_status(void) {
420
421
  gfx_clear();
  empty_serial();
mntmn's avatar
mntmn committed
422

423
  gfx_poke_str(0, 2, "MNT Reform Keyboard");
mntmn's avatar
mntmn committed
424
  gfx_poke_str(0, 3, KBD_FW_REV);
mntmn's avatar
mntmn committed
425
426
  gfx_on();
  gfx_flush();
427

mntmn's avatar
mntmn committed
428
#ifndef KBD_VARIANT_STANDALONE
429
430
  int ok = remote_try_command("s", 1);
  if (!ok) return;
mntmn's avatar
mntmn committed
431
#endif
mntmn's avatar
mntmn committed
432
433
}

434
435
436
437
int oledbrt=0;
void oled_brightness_inc(void) {
  oledbrt+=10;
  if (oledbrt>=0xff) oledbrt = 0xff;
438
  gfx_contrast(oledbrt);
439
440
441
442
}
void oled_brightness_dec(void) {
  oledbrt-=10;
  if (oledbrt<0) oledbrt = 0;
443
  gfx_contrast(oledbrt);
444
445
446
}

int16_t pwmval = 8;
mntmn's avatar
mntmn committed
447

448
void kbd_brightness_init(void) {
449
450
  // initial brightness
  OCR0A = pwmval;
mntmn's avatar
mntmn committed
451

mntmn's avatar
mntmn committed
452
453
454
455
456
457
458
  // clear/set, WGM1:0 set (Phase correct PWM)
  TCCR0A = (1 << 7) | (0 << 6) | (0<<1) | 1;

  // 3=WGM02, (cs02 2:0 -> clock/256 = 100)
  TCCR0B = /*(1 << 3) |*/ (1 << 0) | (0 << 1) | 1;
}

459
void kbd_brightness_inc(void) {
mntmn's avatar
mntmn committed
460
461
462
463
464
  pwmval+=2;
  if (pwmval>=10) pwmval = 10;
  OCR0A = pwmval;
}

465
void kbd_brightness_dec(void) {
mntmn's avatar
mntmn committed
466
467
468
469
470
  pwmval-=2;
  if (pwmval<0) pwmval = 0;
  OCR0A = pwmval;
}

471
void kbd_brightness_set(int brite) {
472
  pwmval = brite;
473
474
475
476
477
  if (pwmval<0) pwmval = 0;
  if (pwmval>=10) pwmval = 10;
  OCR0A = pwmval;
}

478
void remote_turn_on_som(void) {
479
  gfx_clear();
480

481
482
  int ok = remote_try_command("1p", 0);
  if (!ok) return;
483

484
485
  anim_hello();
  kbd_brightness_init();
486
487

  remote_som_power_expected_state = 1;
488
489
}

490
491
492
void remote_turn_off_som(void) {
  anim_goodbye();

493
494
  int ok = remote_try_command("0p", 0);
  if (!ok) return;
495
496

  remote_som_power_expected_state = 0;
mntmn's avatar
mntmn committed
497
498
499
}

void remote_reset_som(void) {
500
501
  int ok = remote_try_command("2p", 0);
  if (!ok) return;
502
}
503

504
void remote_wake_som(void) {
505
506
507
508
509
510
  int ok = remote_try_command("1w", 0);
  if (!ok) return;
  ok = remote_try_command("0w", 0);
  if (!ok) return;
}

mntmn's avatar
mntmn committed
511
void remote_turn_off_aux(void) {
512
513
  int ok = remote_try_command("3p", 0);
  if (!ok) return;
mntmn's avatar
mntmn committed
514
515
516
}

void remote_turn_on_aux(void) {
517
518
  int ok = remote_try_command("4p", 0);
  if (!ok) return;
mntmn's avatar
mntmn committed
519
520
}

521
void remote_report_voltages(void) {
522
523
  int ok = remote_try_command("0c", 0);
  if (!ok) return;
524
525
526
}

void remote_enable_som_uart(void) {
527
528
  int ok = remote_try_command("1u", 0);
  if (!ok) return;
529
530
531
}

void remote_disable_som_uart(void) {
532
533
  int ok = remote_try_command("0u", 0);
  if (!ok) return;
534
535
}

mntmn's avatar
mntmn committed
536
537
538
539
540
541
542
543
544
545
546
547
548
549
typedef struct MenuItem {
  char* title;
  int keycode;
} MenuItem;

#ifdef KBD_VARIANT_STANDALONE
#define MENU_NUM_ITEMS 4
const MenuItem menu_items[] = {
  { "Exit Menu         ESC", KEY_ESCAPE },
  { "Key Backlight-     F1", KEY_F1 },
  { "Key Backlight+     F2", KEY_F2 },
  { "System Status       s", KEY_S }
};
#else
mntmn's avatar
mntmn committed
550
#define MENU_NUM_ITEMS 9
mntmn's avatar
mntmn committed
551
552
553
554
555
556
557
558
const MenuItem menu_items[] = {
  { "Exit Menu         ESC", KEY_ESCAPE },
  { "Power On            1", KEY_1 },
  { "Power Off           0", KEY_0 },
  { "Reset               r", KEY_R },
  { "Battery Status      b", KEY_B },
  { "Key Backlight-     F1", KEY_F1 },
  { "Key Backlight+     F2", KEY_F2 },
559
  { "Wake              SPC", KEY_SPACE },
560
  { "System Status       s", KEY_S },
561
562
563
564

  // Only needed for debugging.
  // The keyboard will go to sleep when turning off
  // main system power.
565
  { "KBD Power-Off       p", KEY_P },
mntmn's avatar
mntmn committed
566
567
568
};
#endif

mntmn's avatar
mntmn committed
569
570
571
572
int current_menu_y = 0;
int current_scroll_y = 0;
int active_meta_mode = 0;

mntmn's avatar
mntmn committed
573
574
int execute_meta_function(int keycode);

mntmn's avatar
mntmn committed
575
576
577
void render_menu(int y) {
  gfx_clear();
  gfx_invert_row(current_menu_y-y);
mntmn's avatar
mntmn committed
578
579
580
  for (int i=0; i<MENU_NUM_ITEMS; i++) {
    gfx_poke_str(0,i-y,menu_items[i].title);
  }
581
582
  gfx_on();
  gfx_flush();
mntmn's avatar
mntmn committed
583
584
585
}

int execute_menu_function(int y) {
mntmn's avatar
mntmn committed
586
587
588
  if (y>=0 && y<MENU_NUM_ITEMS) {
    return execute_meta_function(menu_items[y].keycode);
  }
mntmn's avatar
mntmn committed
589
590
591
592
593
594
  return execute_meta_function(KEY_ESCAPE);
}

// returns 1 for navigation function (stay in meta mode), 0 for terminal function
int execute_meta_function(int keycode) {
  if (keycode == KEY_0) {
mntmn's avatar
mntmn committed
595
    // TODO: are you sure?
mntmn's avatar
mntmn committed
596
    remote_turn_off_som();
597
    EnterPowerOff();
598
599
    // Directly enter menu again
    return 2;
mntmn's avatar
mntmn committed
600
601
602
  }
  else if (keycode == KEY_1) {
    remote_turn_on_som();
603
    return 0;
mntmn's avatar
mntmn committed
604
  }
mntmn's avatar
mntmn committed
605
606
607
608
  else if (keycode == KEY_R) {
    // TODO: are you sure?
    remote_reset_som();
  }
609
610
611
  else if (keycode == KEY_SPACE) {
    remote_wake_som();
  }
612
  /*else if (keycode == KEY_V) {
mntmn's avatar
mntmn committed
613
    remote_turn_off_aux();
614
  }*/
615
  else if (keycode == KEY_B) {
mntmn's avatar
mntmn committed
616
    remote_get_voltages();
617
    return 0;
mntmn's avatar
mntmn committed
618
619
620
  }
  else if (keycode == KEY_S) {
    remote_get_status();
621
    return 0;
mntmn's avatar
mntmn committed
622
623
624
  }
  else if (keycode == KEY_F1) {
    kbd_brightness_dec();
mntmn's avatar
mntmn committed
625
    return 1;
mntmn's avatar
mntmn committed
626
627
628
  }
  else if (keycode == KEY_F2) {
    kbd_brightness_inc();
mntmn's avatar
mntmn committed
629
    return 1;
mntmn's avatar
mntmn committed
630
631
632
633
634
635
636
637
638
639
640
  }
  else if (keycode == HID_KEYBOARD_SC_UP_ARROW) {
    current_menu_y--;
    if (current_menu_y<0) current_menu_y = 0;
    if (current_menu_y<=current_scroll_y) current_scroll_y--;
    if (current_scroll_y<0) current_scroll_y = 0;
    render_menu(current_scroll_y);
    return 1;
  }
  else if (keycode == HID_KEYBOARD_SC_DOWN_ARROW) {
    current_menu_y++;
mntmn's avatar
mntmn committed
641
    if (current_menu_y>=MENU_NUM_ITEMS) current_menu_y = MENU_NUM_ITEMS-1;
mntmn's avatar
mntmn committed
642
643
644
645
646
647
648
649
650
    if (current_menu_y>=current_scroll_y+3) current_scroll_y++;
    render_menu(current_scroll_y);
    return 1;
  }
  else if (keycode == KEY_ENTER) {
    return execute_menu_function(current_menu_y);
  }
  else if (keycode == KEY_ESCAPE) {
    gfx_clear();
651
    gfx_flush();
mntmn's avatar
mntmn committed
652
  }
653
654
  else if (keycode == KEY_P) {
    EnterPowerOff();
655
656
    // Directly enter menu again
    return 2;
657
  }
mntmn's avatar
mntmn committed
658

659
  gfx_clear();
660
  gfx_flush();
661

mntmn's avatar
mntmn committed
662
663
  return 0;
}
664

mntmn's avatar
mntmn committed
665
uint8_t last_meta_key = 0;
mntmn's avatar
mntmn committed
666

667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
// enter the menu
void enter_meta_mode(void) {
  current_scroll_y = 0;
  current_menu_y = 0;
  active_meta_mode = 1;
  // render menu
  render_menu(current_scroll_y);
}

void reset_keyboard_state(void) {
  for (int i=0; i<COLS*ROWS; i++) {
    matrix_debounce[i] = 0;
    matrix_state[i] = 0;
  }
  last_meta_key = 0;
}

mntmn's avatar
mntmn committed
684
685
void process_keyboard(char usb_report_mode, USB_KeyboardReport_Data_t* KeyboardReport) {
  // how many keys are pressed this round
mntmn's avatar
mntmn committed
686
687
  uint8_t total_pressed = 0;
  uint8_t used_key_codes = 0;
mntmn's avatar
mntmn committed
688
689

  // pull ROWs low one after the other
690
  for (int y=0; y<ROWS; y++) {
mntmn's avatar
mntmn committed
691
692
693
694
695
696
697
698
699
    switch (y) {
    case 0: output_low(PORTB, 6); break;
    case 1: output_low(PORTB, 5); break;
    case 2: output_low(PORTB, 4); break;
    case 3: output_low(PORTD, 7); break;
    case 4: output_low(PORTD, 6); break;
    case 5: output_low(PORTD, 4); break;
    }

700
    // wait for signal to stabilize
mntmn's avatar
mntmn committed
701
    // TODO maybe not necessary
702
703
    _delay_us(10);

704
    // check input COLs
mntmn's avatar
mntmn committed
705
    for (int x=0; x<14; x++) {
706
      uint16_t loc = y*COLS+x;
707
      uint16_t keycode = matrix[loc];
mntmn's avatar
mntmn committed
708
      uint8_t  pressed = 0;
mntmn's avatar
mntmn committed
709
      uint8_t  debounced_pressed = 0;
mntmn's avatar
mntmn committed
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728

      // column pins are all over the place
      switch (x) {
      case 0:  pressed = !(PIND&(1<<5)); break;
      case 1:  pressed = !(PINF&(1<<7)); break;
      case 2:  pressed = !(PINE&(1<<6)); break;
      case 3:  pressed = !(PINC&(1<<7)); break;
      case 4:  pressed = !(PINB&(1<<3)); break;
      case 5:  pressed = !(PINB&(1<<2)); break;
      case 6:  pressed = !(PINB&(1<<1)); break;
      case 7:  pressed = !(PINB&(1<<0)); break;
      case 8:  pressed = !(PINF&(1<<0)); break;
      case 9:  pressed = !(PINF&(1<<1)); break;
      case 10: pressed = !(PINF&(1<<4)); break;
      case 11: pressed = !(PINF&(1<<5)); break;
      case 12: pressed = !(PINF&(1<<6)); break;
      case 13: pressed = !(PINC&(1<<6)); break;
      }

mntmn's avatar
mntmn committed
729
730
731
732
733
734
735
736
737
738
739
740
      // shift new state as bit into debounce "register"
      matrix_debounce[loc] = (matrix_debounce[loc]<<1)|pressed;

      // if unclear state, we need to keep the last state of the key
      if (matrix_debounce[loc] == 0x00) {
        matrix_state[loc] = 0;
      } else if (matrix_debounce[loc] == 0x01) {
        matrix_state[loc] = 1;
      }
      debounced_pressed = matrix_state[loc];

      if (debounced_pressed) {
mntmn's avatar
mntmn committed
741
        total_pressed++;
742

743
        // circle key?
mntmn's avatar
mntmn committed
744
        if (keycode == HID_KEYBOARD_SC_EXSEL) {
745
          if (!active_meta_mode && !last_meta_key) {
746
            enter_meta_mode();
mntmn's avatar
mntmn committed
747
          }
mntmn's avatar
mntmn committed
748
        } else {
mntmn's avatar
mntmn committed
749
          if (active_meta_mode) {
750
            // not holding the same key?
751
            if (last_meta_key != keycode) {
mntmn's avatar
mntmn committed
752
753
754
755
756
757
758
759
760
              // hyper/circle/menu functions
              int stay_meta = execute_meta_function(keycode);
              // don't repeat action while key is held down
              last_meta_key = keycode;

              // exit meta mode
              if (!stay_meta) {
                active_meta_mode = 0;
              }
761
762
763
764
765
766
767

              // after wake-up from sleep mode, skip further keymap processing
              if (stay_meta == 2) {
                reset_keyboard_state();
                enter_meta_mode();
                return;
              }
768
            }
769
          } else if (!last_meta_key) {
770
            // not meta mode, regular key: report keypress via USB
mntmn's avatar
mntmn committed
771
            // 6 keys is a hard limit in the HID descriptor :/
mntmn's avatar
mntmn committed
772
773
            if (usb_report_mode && KeyboardReport && used_key_codes<6) {
              KeyboardReport->KeyCode[used_key_codes++] = keycode;
774
775
776
            }
          }
        }
mntmn's avatar
mntmn committed
777
778
779
780
781
782
783
784
785
786
787
788
789
      }
    }

    switch (y) {
    case 0: output_high(PORTB, 6); break;
    case 1: output_high(PORTB, 5); break;
    case 2: output_high(PORTB, 4); break;
    case 3: output_high(PORTD, 7); break;
    case 4: output_high(PORTD, 6); break;
    case 5: output_high(PORTD, 4); break;
    }
  }

790
  // if no more keys are held down, allow a new meta command
mntmn's avatar
mntmn committed
791
  if (total_pressed<1) last_meta_key = 0;
mntmn's avatar
mntmn committed
792
793
}

794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
int blink = 0;

void process_alerts(void) {
  if (low_battery_alert) {
    gfx_on();
    for (int x=8;x<=11;x++) {
      gfx_poke( x,0,' ');
    }
    if (blink) {
      gfx_poke( 9,0,4*32+2);
      gfx_poke(10,0,4*32+3);
    }
    gfx_flush();
  }
  blink = 1-blink;
}

mntmn's avatar
mntmn committed
811
812
int main(void)
{
813
#ifdef KBD_VARIANT_QWERTY_US
814
  matrix[COLS*4+1]=KEY_DELETE;
mntmn's avatar
mntmn committed
815
#endif
mntmn's avatar
mntmn committed
816
#ifdef KBD_VARIANT_NEO2
817
818
819
  matrix[COLS*3+0]=HID_KEYBOARD_SC_CAPS_LOCK; // M3
  matrix[COLS*2+13]=KEY_ENTER;
  matrix[COLS*3+13]=KEY_BACKSLASH_AND_PIPE; // M3
mntmn's avatar
mntmn committed
820
#endif
mntmn's avatar
mntmn committed
821

mntmn's avatar
mntmn committed
822
823
  SetupHardware();
  GlobalInterruptEnable();
824
  anim_hello();
mntmn's avatar
mntmn committed
825

826
  int counter = 0;
827

mntmn's avatar
mntmn committed
828
829
830
831
832
  for (;;)
  {
    process_keyboard(0, NULL);
    HID_Device_USBTask(&Keyboard_HID_Interface);
    USB_USBTask();
833
    counter++;
834
#ifndef KBD_VARIANT_STANDALONE
835
      if (counter>=100000) {
836
837
838
839
840
841
        remote_check_for_low_battery();
        counter = 0;
      }
      if (counter%750 == 0) {
        process_alerts();
      }
842
#endif
mntmn's avatar
mntmn committed
843
844
845
  }
}

846
void SetupHardware(void)
mntmn's avatar
mntmn committed
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
{
  // Disable watchdog if enabled by bootloader/fuses
  MCUSR &= ~(1 << WDRF);
  wdt_disable();

  // Disable clock division
  clock_prescale_set(clock_div_1);

  // declare port pins as inputs (0) and outputs (1)
  DDRB  = 0b11110000;
  DDRC  = 0b00000000;
  DDRD  = 0b11011001;
  DDRE  = 0b00000000;
  DDRF  = 0b00000000;

  // initial pin states
  PORTB = 0b10001111;
  PORTC = 0b11000000;
  PORTD = 0b00100000;
  PORTE = 0b01000000;
  PORTF = 0b11111111;
mntmn's avatar
mntmn committed
868

mntmn's avatar
mntmn committed
869
870
871
872
  // disable JTAG
  MCUCR |=(1<<JTD);
  MCUCR |=(1<<JTD);

mntmn's avatar
mntmn committed
873
  kbd_brightness_init();
874
  gfx_init(false);
mntmn's avatar
mntmn committed
875

876
  Serial_Init(57600, false);
mntmn's avatar
mntmn committed
877
878
879
  USB_Init();
}

880
881
882
883
884
885
886
887
888
889
890
891
/* Setup the AVR to enter the Power-Down state to greatly save power.
 * Configures all outputs to be in the low state if possible, and disables
 * services like USB and Serial.
 *
 * Will leave the ports setup so that the Circle key row is being scanned
 * so when the watchdog wakes up it can quickly check and go back to sleep if not
 * Added by Chartreuse - 2021/08/14
 *
 */
void EnterPowerOff(void)
{
  USB_Disable(); // Stop USB stack so it doesn't wake us up
892

893
894
895
  // turn off backlight, but don't overwrite setting
  OCR0A = 0;

896
897
898
899
900
  // Turn off OLED to save power
  gfx_clear_screen();
  gfx_off();
  // Disable ADC to save even more power
  ADCSRA=0;
901

902
  cli();    // No interrupts
903
904
905

  // Set all ports not floating if possible, leaving pullups alone
  PORTB=0x3F; // Leave pull-up on all the columns on PB0-3, drive rows 2-3 high, 1-low
906
  PORTC=0xC0;
907
908
909
910
911
912
  PORTD=0xF0; // Keep pullup on PD5 like setup did, drive rows 4,5,6 high
  PORTE=0x40; // Pullup on PE6
  PORTF=0xFF; // Pullups on PF (columns)
  // ROW1 is the only row driven low and left low, thus is always ready to be read out
  // We just need to check COL14 (PC6) if it is low (pressed) or high

913
  // Unfortunately the circle key is on COL14(PC6) which doesn't have pin change interrupt
914
915
916
  // capabilities, so we need to wake up every so often to check if it is pressed, and
  // if so bring us out of power-off
  // We can use the Watchdog timer to do this.
917

918
919
920
  do {
    wdt_reset();
    WDTCSR = (1<<WDCE) | (1<<WDE); // Enable writes to watchdog
921
    WDTCSR = (1<<WDIE) | (1<<WDE) | (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (0<<WDP0); // Interrupt mode, 1s timeout
922
923
924
925
926
927
928
929

    // Enter Power-save mode
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sei();              // Enable interrupts so we can actually wake
    sleep_cpu();        // Actually go to sleep
    // Zzzzzz
    sleep_disable();    // We've woken up
930
    sei();
931
932
933
    // Check if circle key has been pressed (active-low)
    // If not reset the watchdog and try again
  } while(PINC&(1<<6));
934

935
936
937
938
939
940
941
942
943
944
945
  // Resume and reinitialize hardware
  SetupHardware();
}

ISR(WDT_vect)
{
  // WDT interrupt enable and flag cleared on entry
  wdt_disable(); // Disable watchdog for now
}


mntmn's avatar
mntmn committed
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
/** Event handler for the library USB Connection event. */
void EVENT_USB_Device_Connect(void)
{
}

/** Event handler for the library USB Disconnection event. */
void EVENT_USB_Device_Disconnect(void)
{
}

/** Event handler for the library USB Configuration Changed event. */
void EVENT_USB_Device_ConfigurationChanged(void)
{
  bool ConfigSuccess = true;

  ConfigSuccess &= HID_Device_ConfigureEndpoints(&Keyboard_HID_Interface);

  USB_Device_EnableSOFEvents();
}

/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void)
{
  HID_Device_ProcessControlRequest(&Keyboard_HID_Interface);
}

/** Event handler for the USB device Start Of Frame event. */
void EVENT_USB_Device_StartOfFrame(void)
{
  HID_Device_MillisecondElapsed(&Keyboard_HID_Interface);
}

/** HID class driver callback function for the creation of HID reports to the host.
 *
 *  \param[in]     HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
 *  \param[in,out] ReportID    Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID
 *  \param[in]     ReportType  Type of the report to create, either HID_REPORT_ITEM_In or HID_REPORT_ITEM_Feature
 *  \param[out]    ReportData  Pointer to a buffer where the created report should be stored
 *  \param[out]    ReportSize  Number of bytes written in the report (or zero if no report is to be sent)
 *
 *  \return Boolean \c true to force the sending of the report, \c false to let the library determine if it needs to be sent
 */

bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                         uint8_t* const ReportID,
                                         const uint8_t ReportType,
                                         void* ReportData,
                                         uint16_t* const ReportSize)
{
  USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData;

  process_keyboard(1, KeyboardReport);
mntmn's avatar
mntmn committed
998

mntmn's avatar
mntmn committed
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
  *ReportSize = sizeof(USB_KeyboardReport_Data_t);
  return false;
}

/** HID class driver callback function for the processing of HID reports from the host.
 *
 *  \param[in] HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
 *  \param[in] ReportID    Report ID of the received report from the host
 *  \param[in] ReportType  The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature
 *  \param[in] ReportData  Pointer to a buffer where the received report has been stored
 *  \param[in] ReportSize  Size in bytes of the received HID report
 */
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                          const uint8_t ReportID,
                                          const uint8_t ReportType,
                                          const void* ReportData,
                                          const uint16_t ReportSize)
{
mntmn's avatar
mntmn committed
1017
  uint8_t* data = (uint8_t*)ReportData;
1018
1019
1020
  if (ReportSize<4) return;

  if (data[0]=='O' && data[1]=='L' && data[2]=='E' && data[3]=='D') {
1021
1022
    // OLED: write characters on display
    gfx_on();
1023
1024
1025
1026
    for (int y=0; y<4; y++) {
      for (int x=0; x<21; x++) {
        gfx_poke(x,y,data[4+y*21+x]);
      }
mntmn's avatar
mntmn committed
1027
    }
1028
1029
1030
1031
1032
    gfx_flush();
  }
  if (data[0]=='O' && data[1]=='I' && data[2]=='N' && data[3]=='V') {
    gfx_clear_invert();
    gfx_invert_row(data[4]-'0');
1033
  }
1034
1035
1036
1037
1038
1039
1040
  else if (data[0]=='L' && data[1]=='I' && data[2]=='T' && data[3]=='E') {
    char brite = data[4]-'0';
    brite++;
    if (brite<=1) brite=0;
    if (brite>9) brite=9;
    kbd_brightness_set(brite);
  }
1041
  else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='0') {
1042
    // PWR0: shutdown (turn off power rails)
1043
    remote_turn_off_som();
1044
    EnterPowerOff();
1045
    reset_keyboard_state();
1046
1047
  }
  else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='3') {
1048
    // PWR3: aux power off
1049
    remote_turn_off_aux();
1050
1051
  }
  else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='4') {
1052
    // PWR4: aux power on
1053
    remote_turn_on_aux();
1054
1055
  }
  else if (data[0]=='U' && data[1]=='A' && data[2]=='R' && data[3]=='1') {
1056
    // UAR1: UART reporting on
1057
    remote_enable_som_uart();
1058
1059
  }
  else if (data[0]=='U' && data[1]=='A' && data[2]=='R' && data[3]=='0') {
1060
    // UAR0: UART reporting off
1061
1062
1063
    remote_disable_som_uart();
  }
  else if (data[0]=='R' && data[1]=='P' && data[2]=='R' && data[3]=='T') {
1064
    // RPRT: Report power stats to UART
1065
    remote_report_voltages();
mntmn's avatar
mntmn committed
1066
  }
mntmn's avatar
mntmn committed
1067
}