From c966e7982c4fa42d7dae6b4f4dca6966b695a973 Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Thu, 22 Oct 2015 13:37:45 -0400 Subject: [PATCH] bluetooth --- common.mk | 4 + .../extended_keymap_default.c | 2 +- keyboard/atomic/flash-pcb.sh | 2 +- keyboard/planck/Makefile | 22 +- keyboard/planck/config.h | 71 ++- keyboard/planck/keymap_midi.c | 44 +- keyboard/planck/keymaps/keymap_lock.c | 41 +- keyboard/planck/planck_with_bootloader.hex | Bin 0 -> 80584 bytes keyboard/preonic/Makefile | 3 +- keyboard/preonic/beeps.c | 432 +++++++++--------- keyboard/preonic/beeps.h | 5 +- .../extended_keymaps/extended_keymap_lock.c | 2 +- keyboard/preonic/keymap_midi.c | 92 +++- keyboard/preonic/keymap_midi.h | 6 +- protocol/bluefruit.mk | 2 +- protocol/bluefruit/bluefruit.c | 15 +- protocol/bluefruit/bluefruit.h | 5 +- protocol/bluefruit/main.c | 98 ++-- protocol/lufa/descriptor.c | 2 +- protocol/lufa/lufa.c | 4 +- 20 files changed, 533 insertions(+), 319 deletions(-) create mode 100755 keyboard/planck/planck_with_bootloader.hex diff --git a/common.mk b/common.mk index e61ae69e5e..3237e539a6 100644 --- a/common.mk +++ b/common.mk @@ -68,6 +68,10 @@ ifdef BACKLIGHT_ENABLE OPT_DEFS += -DBACKLIGHT_ENABLE endif +ifdef BLUETOOTH_ENABLE + OPT_DEFS += -DBLUETOOTH_ENABLE +endif + ifdef KEYMAP_SECTION_ENABLE OPT_DEFS += -DKEYMAP_SECTION_ENABLE EXTRALDFLAGS = -Wl,-L$(TOP_DIR),-Tldscript_keymap_avr5.x diff --git a/keyboard/atomic/extended_keymaps/extended_keymap_default.c b/keyboard/atomic/extended_keymaps/extended_keymap_default.c index e662c91889..ab08bb6d25 100644 --- a/keyboard/atomic/extended_keymaps/extended_keymap_default.c +++ b/keyboard/atomic/extended_keymaps/extended_keymap_default.c @@ -39,7 +39,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { // ┌─ 2u ────────────┐ // │ X │ // └─────────────────┘ - { KC_LCTL, KC_LGUI, KC_LALT, KC_LGUI, KC_LCTL, KC_SPC, KC_SPC, KC_RALT, KC_RGUI, KC_RALT, KC_RGUI, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT }, + { BL_STEP, KC_LCTL, KC_LALT, KC_LGUI, KC_LGUI, KC_SPC, KC_SPC, KC_RGUI, KC_RGUI, KC_RALT, KC_RCTL, BL_STEP, KC_LEFT, KC_DOWN, KC_RGHT }, // ┌────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┬────────┐ // │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ // └────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┴────────┘ diff --git a/keyboard/atomic/flash-pcb.sh b/keyboard/atomic/flash-pcb.sh index 2ae15458c0..46abc6982a 100755 --- a/keyboard/atomic/flash-pcb.sh +++ b/keyboard/atomic/flash-pcb.sh @@ -1,3 +1,3 @@ dfu-programmer atmega32u4 erase --force -dfu-programmer atmega32u4 flash planck_pcb.hex +dfu-programmer atmega32u4 flash atomic_pcb.hex dfu-programmer atmega32u4 reset \ No newline at end of file diff --git a/keyboard/planck/Makefile b/keyboard/planck/Makefile index 2f0ba5ba9d..d9e8ce7889 100644 --- a/keyboard/planck/Makefile +++ b/keyboard/planck/Makefile @@ -121,15 +121,16 @@ EXTRAKEY_ENABLE = yes # Audio control and System control(+450) CONSOLE_ENABLE = yes # Console for debug(+400) COMMAND_ENABLE = yes # Commands for debug and configuration # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE -#SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend +# SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend # NKRO_ENABLE = yes # USB Nkey Rollover - not yet supported in LUFA BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality -MIDI_ENABLE = YES # MIDI controls +# MIDI_ENABLE = YES # MIDI controls # UNICODE_ENABLE = YES # Unicode +# BLUETOOTH_ENABLE = yes # Enable Bluetooth with the Adafruit EZ-Key HID ifdef MIDI_ENABLE - SRC += keymap_midi.c \ - beeps.c + SRC += keymap_midi.c + # beeps.c endif ifdef UNICODE_ENABLE @@ -143,7 +144,18 @@ endif VPATH += $(TARGET_DIR) VPATH += $(TOP_DIR) -include $(TOP_DIR)/protocol/lufa.mk + + +ifdef BLUETOOTH_ENABLE + BLUEFRUIT_TRACE_SERIAL=true + + include $(TOP_DIR)/protocol.mk + include $(TOP_DIR)/protocol/bluefruit.mk + include $(TOP_DIR)/protocol.mk +else + include $(TOP_DIR)/protocol/lufa.mk +endif + include $(TOP_DIR)/common.mk include $(TOP_DIR)/rules.mk diff --git a/keyboard/planck/config.h b/keyboard/planck/config.h index e970127703..0a1a6a9357 100644 --- a/keyboard/planck/config.h +++ b/keyboard/planck/config.h @@ -58,16 +58,83 @@ along with this program. If not, see . keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \ ) +#ifdef BLUETOOTH_ENABLE +#ifdef __AVR_ATmega32U4__ + #define SERIAL_UART_BAUD 9600 + #define SERIAL_UART_DATA UDR1 + #define SERIAL_UART_UBRR ((F_CPU/(16UL*SERIAL_UART_BAUD))-1) + #define SERIAL_UART_RXD_VECT USART1_RX_vect + #define SERIAL_UART_TXD_READY (UCSR1A&(1<>8); /* baud rate */ \ + UCSR1B = (1<event.key.col == (MATRIX_COLS - 1) && record->event.key.row == (MATRIX_ROWS - 1)) { if (record->event.pressed) { starting_note++; - // play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); + play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); midi_send_cc(&midi_device, 0, 0x7B, 0); midi_send_cc(&midi_device, 1, 0x7B, 0); midi_send_cc(&midi_device, 2, 0x7B, 0); @@ -43,15 +43,15 @@ void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) midi_send_cc(&midi_device, 4, 0x7B, 0); return; } else { - // stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1))); - // stop_all_notes(); + stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1))); + stop_all_notes(); return; } } if (record->event.key.col == (MATRIX_COLS - 2) && record->event.key.row == (MATRIX_ROWS - 1)) { if (record->event.pressed) { starting_note--; - // play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); + play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); midi_send_cc(&midi_device, 0, 0x7B, 0); midi_send_cc(&midi_device, 1, 0x7B, 0); midi_send_cc(&midi_device, 2, 0x7B, 0); @@ -59,8 +59,8 @@ void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) midi_send_cc(&midi_device, 4, 0x7B, 0); return; } else { - // stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1))); - // stop_all_notes(); + stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1))); + stop_all_notes(); return; } } @@ -72,13 +72,13 @@ void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) midi_send_cc(&midi_device, 2, 0x7B, 0); midi_send_cc(&midi_device, 3, 0x7B, 0); midi_send_cc(&midi_device, 4, 0x7B, 0); - // stop_all_notes(); - // for (int i = 0; i <= 7; i++) { - // play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); - // _delay_us(80000); - // stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1))); - // _delay_us(8000); - // } + stop_all_notes(); + for (int i = 0; i <= 7; i++) { + play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); + _delay_us(80000); + stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1))); + _delay_us(8000); + } return; } if (record->event.key.col == (MATRIX_COLS - 4) && record->event.key.row == (MATRIX_ROWS - 1) && record->event.pressed) { @@ -88,23 +88,23 @@ void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) midi_send_cc(&midi_device, 2, 0x7B, 0); midi_send_cc(&midi_device, 3, 0x7B, 0); midi_send_cc(&midi_device, 4, 0x7B, 0); - // stop_all_notes(); - // for (int i = 0; i <= 7; i++) { - // play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); - // _delay_us(80000); - // stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1))); - // _delay_us(8000); - // } + stop_all_notes(); + for (int i = 0; i <= 7; i++) { + play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); + _delay_us(80000); + stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1))); + _delay_us(8000); + } return; } if (record->event.pressed) { // midi_send_noteon(&midi_device, record->event.key.row, starting_note + SCALE[record->event.key.col], 127); midi_send_noteon(&midi_device, 0, (starting_note + SCALE[record->event.key.col + offset])+12*(MATRIX_ROWS - record->event.key.row), 127); - // play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row)), 0xF); + play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row)), 0xF); } else { // midi_send_noteoff(&midi_device, record->event.key.row, starting_note + SCALE[record->event.key.col], 127); midi_send_noteoff(&midi_device, 0, (starting_note + SCALE[record->event.key.col + offset])+12*(MATRIX_ROWS - record->event.key.row), 127); - // stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row))); + stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row))); } } \ No newline at end of file diff --git a/keyboard/planck/keymaps/keymap_lock.c b/keyboard/planck/keymaps/keymap_lock.c index 4fe2e0a2bf..b844d1cdab 100644 --- a/keyboard/planck/keymaps/keymap_lock.c +++ b/keyboard/planck/keymaps/keymap_lock.c @@ -2,7 +2,7 @@ #include "backlight.h" #include "action_layer.h" #include "keymap_midi.h" -#include "beeps.h" +#include const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [0] = { /* Qwerty */ @@ -53,6 +53,16 @@ const uint16_t PROGMEM fn_actions[] = { }; +uint16_t hextokeycode(int hex) { + if (hex == 0x0) { + return KC_0; + } else if (hex < 0xA) { + return KC_1 + (hex - 0x1); + } else { + return KC_A + (hex - 0xA); + } +} + const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { // MACRODOWN only works in this function @@ -64,6 +74,35 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) backlight_set(BACKLIGHT_LEVELS); default_layer_and(0); default_layer_or((1<<5)); + + uint8_t low = boot_lock_fuse_bits_get(0x0000); + uint8_t high = boot_lock_fuse_bits_get(0x0003); + uint8_t ext = boot_lock_fuse_bits_get(0x0002); + uint8_t lock = boot_lock_fuse_bits_get(0x0001); + + register_code(hextokeycode((low & 0xF0) >> 4)); + unregister_code(hextokeycode((low & 0xF0) >> 4)); + register_code(hextokeycode((low & 0x0F))); + unregister_code(hextokeycode((low & 0x0F))); + + + register_code(hextokeycode((high & 0xF0) >> 4)); + unregister_code(hextokeycode((high & 0xF0) >> 4)); + register_code(hextokeycode((high & 0x0F))); + unregister_code(hextokeycode((high & 0x0F))); + + + register_code(hextokeycode((ext & 0xF0) >> 4)); + unregister_code(hextokeycode((ext & 0xF0) >> 4)); + register_code(hextokeycode((ext & 0x0F))); + unregister_code(hextokeycode((ext & 0x0F))); + + + register_code(hextokeycode((lock & 0xF0) >> 4)); + unregister_code(hextokeycode((lock & 0xF0) >> 4)); + register_code(hextokeycode((lock & 0x0F))); + unregister_code(hextokeycode((lock & 0x0F))); + // note(0+12, 20); // note(0+24, 20); } else { diff --git a/keyboard/planck/planck_with_bootloader.hex b/keyboard/planck/planck_with_bootloader.hex new file mode 100755 index 0000000000000000000000000000000000000000..c61dfb743a2620716c7b645edf15566a3252d6cc GIT binary patch literal 80584 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 literal 0 HcmV?d00001 diff --git a/keyboard/preonic/Makefile b/keyboard/preonic/Makefile index 40bd36aecf..0145e44bc6 100644 --- a/keyboard/preonic/Makefile +++ b/keyboard/preonic/Makefile @@ -127,7 +127,8 @@ MIDI_ENABLE = yes # MIDI controls BACKLIGHT_ENABLE = yes ifdef MIDI_ENABLE - SRC += keymap_midi.c + SRC += keymap_midi.c \ + beeps.c endif # Optimize size but this may cause error "relocation truncated to fit" diff --git a/keyboard/preonic/beeps.c b/keyboard/preonic/beeps.c index 13e46e1daf..8ffd70f417 100644 --- a/keyboard/preonic/beeps.c +++ b/keyboard/preonic/beeps.c @@ -5,31 +5,6 @@ #include #define PI 3.14159265 -#define CHANNEL OCR1C - -volatile uint16_t sample; -uint16_t lastSample; - -const int sounddata_length=200; - -const unsigned char sounddata_data[] PROGMEM = {128, -128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, -128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, -128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, -128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, -128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, -128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, -128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, -128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, -128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, -128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 127, 129, 128, 127, 133, -117, 109, 125, 121, 116, 132, 140, 126, 114, 114, 116, 120, 114, 93, 73, 66, 76, 116, 142, 129, -128, 129, 120, 119, 118, 104, 87, 123, 181, 194, 196, 198, 189, 176, 160, 162, 172, 164, 164, 183, -197, 188, 168, 167, 170, 165, 185, 209, 206, 196, 196, 199, 185, 162, 156, 167, 176, 173, 170, 166, -151, 142, 140, 134, 130, 127, 113, 86, 67, 66, 69, 75, 73, 75, 86, 90, 91, 84, 65, 48, -41, 30, 26, 56, 91, 88, 72, 70, 73, 82, 89, 73, 57, 60, 74, 89, 92, 77, 63, 60, -53, 47, 56, 64, 63, 61, 56, 54, 52, 36, 16, 22, 51, 66, 67, 70, 76, 88, 99, 92, -77, 74, 85, 100, 106, 97, 83, 85, 96, 108, 133, 160, 164}; void delay_us(int count) { while(count--) { @@ -37,202 +12,235 @@ void delay_us(int count) { } } +int voices = 0; +double frequency = 0; +int volume = 0; +int position = 0; + +double frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +bool sliding = false; +#define RANGE 1000 +volatile int i=0; //elements of the wave + + void beeps() { - // DDRB |= (1<<7); - // PORTB &= ~(1<<7); - - // // Use full 16-bit resolution. - // ICR1 = 0xFFFF; - - // // I could write a wall of text here to explain... but TL;DW - // // Go read the ATmega32u4 datasheet. - // // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on - - // // Pin PB7 = OCR1C (Timer 1, Channel C) - // // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0 - // // (i.e. start high, go low when counter matches.) - // // WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0 - // // Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1 - - // TCCR1A = _BV(COM1C1) | _BV(WGM11); // = 0b00001010; - // TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001; - - - // // Turn off PWM control on PB7, revert to output low. - // // TCCR1A &= ~(_BV(COM1C1)); - // // CHANNEL = ((1 << level) - 1); - - // // Turn on PWM control of PB7 - // TCCR1A |= _BV(COM1C1); - // // CHANNEL = level << OFFSET | 0x0FFF; - // // CHANNEL = 0b1010101010101010; - - // float x = 12; - // float y = 24; - // float length = 50; - // float scale = 1; - - // // int f1 = 1000000/440; - // // int f2 = 1000000/880; - // // for (uint32_t i = 0; i < length * 1000; i++) { - // // // int frequency = 1/((sin(PI*2*i*scale*pow(2, x/12.0))*.5+1 + sin(PI*2*i*scale*pow(2, y/12.0))*.5+1) / 2); - - // // ICR1 = f1; // Set max to the period - // // OCR1C = f1 >> 1; // Set compare to half the period - // // // _delay_us(10); - // // } - // int frequency = 1000000/440; - // ICR1 = frequency; // Set max to the period - // OCR1C = frequency >> 1; // Set compare to half the period - // _delay_us(500000); - - // TCCR1A &= ~(_BV(COM1C1)); - // CHANNEL = 0; -play_notes(); - - - // play_note(55*pow(2, 0/12.0), 1); - // play_note(55*pow(2, 12/12.0), 1); - // play_note(55*pow(2, 24/12.0), 1); - // play_note(55*pow(2, 0/12.0), 1); - // play_note(55*pow(2, 12/12.0), 1); - // play_note(55*pow(2, 24/12.0), 1); - - // play_note(0, 4); - - // play_note(55*pow(2, 0/12.0), 8); - // play_note(55*pow(2, 12/12.0), 4); - // play_note(55*pow(2, 10/12.0), 4); - // play_note(55*pow(2, 12/12.0), 8); - // play_note(55*pow(2, 10/12.0), 4); - // play_note(55*pow(2, 7/12.0), 2); - // play_note(55*pow(2, 8/12.0), 2); - // play_note(55*pow(2, 7/12.0), 16); - // play_note(0, 4); - // play_note(55*pow(2, 3/12.0), 8); - // play_note(55*pow(2, 5/12.0), 4); - // play_note(55*pow(2, 7/12.0), 4); - // play_note(55*pow(2, 7/12.0), 8); - // play_note(55*pow(2, 5/12.0), 4); - // play_note(55*pow(2, 3/12.0), 4); - // play_note(55*pow(2, 2/12.0), 16); - - + play_notes(); } -void play_note(float freq, int length) { - DDRB |= (1<<7); - PORTB &= ~(1<<7); +void send_freq(double freq, int vol) { + int duty = (((double)F_CPU) / freq); + ICR3 = duty; // Set max to the period + OCR3A = duty >> (0x10 - vol); // Set compare to half the period +} + +void stop_all_notes() { + voices = 0; + TCCR3A = 0; + TCCR3B = 0; + frequency = 0; + volume = 0; + + for (int i = 0; i < 8; i++) { + frequencies[i] = 0; + volumes[i] = 0; + } +} + +void stop_note(double freq) { + for (int i = 7; i >= 0; i--) { + if (frequencies[i] == freq) { + frequencies[i] = 0; + volumes[i] = 0; + for (int j = i; (j < 7); j++) { + frequencies[j] = frequencies[j+1]; + frequencies[j+1] = 0; + volumes[j] = volumes[j+1]; + volumes[j+1] = 0; + } + } + } + voices--; + if (voices < 0) + voices = 0; + if (voices == 0) { + TCCR3A = 0; + TCCR3B = 0; + frequency = 0; + volume = 0; + } else { + double freq = frequencies[voices - 1]; + int vol = volumes[voices - 1]; + if (frequency < freq) { + sliding = true; + for (double f = frequency; f <= freq; f += ((freq - frequency) / 500.0)) { + send_freq(f, vol); + } + sliding = false; + } else if (frequency > freq) { + sliding = true; + for (double f = frequency; f >= freq; f -= ((frequency - freq) / 500.0)) { + send_freq(f, vol); + } + sliding = false; + } + send_freq(freq, vol); + frequency = freq; + volume = vol; + } +} + +void init_notes() { + // TCCR1A = (1 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (1 << WGM10); + // TCCR1B = (1 << COM1B1) | (0 << COM1A0) | (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (0 << CS11) | (1 << CS10); + + // DDRC |= (1<<6); + + // TCCR3A = (1 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); + // TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (0 << CS31) | (1 << CS30); + + // ICR3 = 0xFFFF; + // OCR3A = (int)((float)wave[i]*ICR3/RANGE); //go to next array element + + + // cli(); + + // /* Enable interrupt on timer2 == 127, with clk/8 prescaler. At 16MHz, + // this gives a timer interrupt at 15625Hz. */ + // TIMSK3 = (1 << OCIE3A); + + // /* clear/reset timer on match */ + // // TCCR3A = 1<> 8) & 0x00FF) +// #define lowByte(c) (c & 0x00FF) + +ISR(TIMER3_COMPA_vect) { + + if (ICR3 > 0 && !sliding) { + switch (position) { + case 0: { + int duty = (((double)F_CPU) / (frequency)); + ICR3 = duty; // Set max to the period + OCR3A = duty >> 1; // Set compare to half the period + break; + } + case 1: { + int duty = (((double)F_CPU) / (frequency*2)); + ICR3 = duty; // Set max to the period + OCR3A = duty >> 1; // Set compare to half the period + break; + } + case 2: { + int duty = (((double)F_CPU) / (frequency*3)); + ICR3 = duty; // Set max to the period + OCR3A = duty >> 1; // Set compare to half the period + break; + } + } + position = (position + 1) % 3; + } +// /* OCR2A has been cleared, per TCCR2A above */ +// // OCR3A = 127; + +// // pos1 += incr1; +// // pos2 += incr2; +// // pos3 += incr3; + +// // sample = sinewave[highByte(pos1)] + sinewave[highByte(pos2)] + sinewave[highByte(pos3)]; + +// // OCR3A = sample; + + +// OCR3A=pgm_read_byte(&sinewave[pos1]); +// pos1++; +// // PORTC &= ~(1<<6); + +// /* buffered, 1x gain, active mode */ +// // SPDR = highByte(sample) | 0x70; +// // while (!(SPSR & (1< 0) { - int frequency = 1000000/freq; - ICR1 = frequency; // Set max to the period - OCR1C = frequency >> 1; // Set compare to half the period + DDRC |= (1<<6); - TCCR1A = _BV(COM1C1) | _BV(WGM11); // = 0b00001010; - TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001; - } + TCCR3A = (1 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); + TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30); - for (int i = 0; i < length; i++) { - _delay_us(50000); - } - - TCCR1A &= ~(_BV(COM1C1)); -} - -// This is called at 8000 Hz to load the next sample. -ISR(TIMER1_COMPA_vect) { - if (sample >= sounddata_length) { - if (sample == sounddata_length + lastSample) { - TIMSK1 &= ~_BV(OCIE1A); - - // Disable the per-sample timer completely. - TCCR1B &= ~_BV(CS10); + if (frequency != 0) { + if (frequency < freq) { + for (double f = frequency; f <= freq; f += ((freq - frequency) / 500.0)) { + send_freq(f, vol); + } + } else if (frequency > freq) { + for (double f = frequency; f >= freq; f -= ((frequency - freq) / 500.0)) { + send_freq(f, vol); + } + } } - else { - OCR1C = sounddata_length + lastSample - sample; - } - } - else { - OCR1C = pgm_read_byte(&sounddata_data[sample]); - } + send_freq(freq, vol); + frequency = freq; + volume = vol; - ++sample; + frequencies[voices] = frequency; + volumes[voices] = volume; + voices++; + } + // ICR3 = 0xFFFF; + // for (int i = 0; i < 10000; i++) { + // OCR3A = round((sin(i*freq)*.5)+.5)*0xFFFF; + // // _delay_us(50); + // } + + // TCCR3A = 0; + // TCCR3B = 0; } -void play_notes() { +// void note(int x, float length) { +// DDRC |= (1<<6); +// int t = (int)(440*pow(2,-x/12.0)); // starting note +// for (int y = 0; y < length*1000/t; y++) { // note length +// PORTC |= (1<<6); +// delay_us(t); +// PORTC &= ~(1<<6); +// delay_us(t); +// } +// PORTC &= ~(1<<6); +// } - - // Set up Timer 2 to do pulse width modulation on the speaker - // pin. - - DDRB |= (1<<7); - PORTB &= ~(1<<7); - - // Use internal clock (datasheet p.160) - // ASSR &= ~(_BV(EXCLK) | _BV(AS2)); - - // Set fast PWM mode (p.157) - TCCR1A |= _BV(WGM21) | _BV(WGM20); - TCCR1B &= ~_BV(WGM22); - - // Do non-inverting PWM on pin OC2A (p.155) - // On the Arduino this is pin 11. - TCCR1A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0); - TCCR1A &= ~(_BV(COM2B1) | _BV(COM2B0)); - // No prescaler (p.158) - TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10); - - // Set initial pulse width to the first sample. - OCR1A = pgm_read_byte(&sounddata_data[0]); - - - - - cli(); - - // Set CTC mode (Clear Timer on Compare Match) (p.133) - // Have to set OCR1A *after*, otherwise it gets reset to 0! - TCCR2B = (TCCR2B & ~_BV(WGM13)) | _BV(WGM12); - TCCR2A = TCCR2A & ~(_BV(WGM11) | _BV(WGM10)); - - // No prescaler (p.134) - TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10); - - // Set the compare register (OCR1A). - // OCR1A is a 16-bit register, so we have to do this with - // interrupts disabled to be safe. - // OCR2A = F_CPU / SAMPLE_RATE; // 16e6 / 8000 = 2000 - OCR2A = 2000; - - // Enable interrupt when TCNT1 == OCR1A (p.136) - TIMSK1 |= _BV(OCIE2A); - - sample = 0; - sei(); -} - -void note(int x, float length) { - DDRB |= (1<<1); - int t = (int)(440*pow(2,-x/12.0)); // starting note - for (int y = 0; y < length*1000/t; y++) { // note length - PORTB |= (1<<1); - delay_us(t); - PORTB &= ~(1<<1); - delay_us(t); - } - PORTB &= ~(1<<1); -} - -void true_note(float x, float y, float length) { - for (uint32_t i = 0; i < length * 50; i++) { - uint32_t v = (uint32_t) (round(sin(PI*2*i*640000*pow(2, x/12.0))*.5+1 + sin(PI*2*i*640000*pow(2, y/12.0))*.5+1) / 2 * pow(2, 8)); - for (int u = 0; u < 8; u++) { - if (v & (1 << u) && !(PORTB&(1<<1))) - PORTB |= (1<<1); - else if (PORTB&(1<<1)) - PORTB &= ~(1<<1); - } - } - PORTB &= ~(1<<1); -} \ No newline at end of file +// void true_note(float x, float y, float length) { +// for (uint32_t i = 0; i < length * 50; i++) { +// uint32_t v = (uint32_t) (round(sin(PI*2*i*640000*pow(2, x/12.0))*.5+1 + sin(PI*2*i*640000*pow(2, y/12.0))*.5+1) / 2 * pow(2, 8)); +// for (int u = 0; u < 8; u++) { +// if (v & (1 << u) && !(PORTC&(1<<6))) +// PORTC |= (1<<6); +// else if (PORTC&(1<<6)) +// PORTC &= ~(1<<6); +// } +// } +// PORTC &= ~(1<<6); +// } \ No newline at end of file diff --git a/keyboard/preonic/beeps.h b/keyboard/preonic/beeps.h index 3e3c634ff5..378983c605 100644 --- a/keyboard/preonic/beeps.h +++ b/keyboard/preonic/beeps.h @@ -6,4 +6,7 @@ void note(int x, float length); void beeps(); void true_note(float x, float y, float length); -void play_note(float freq, int length); \ No newline at end of file +void play_note(double freq, int vol); +void stop_note(double freq); +void stop_all_notes(); +void init_notes(); \ No newline at end of file diff --git a/keyboard/preonic/extended_keymaps/extended_keymap_lock.c b/keyboard/preonic/extended_keymaps/extended_keymap_lock.c index 8edf8a283d..ec7bc776ac 100644 --- a/keyboard/preonic/extended_keymaps/extended_keymap_lock.c +++ b/keyboard/preonic/extended_keymaps/extended_keymap_lock.c @@ -1,8 +1,8 @@ #include "extended_keymap_common.h" #include "backlight.h" #include "action_layer.h" -#include "lufa.h" #include "keymap_midi.h" +#include "beeps.h" const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [0] = { /* Qwerty */ diff --git a/keyboard/preonic/keymap_midi.c b/keyboard/preonic/keymap_midi.c index c7937bb9b5..a0fa59990c 100644 --- a/keyboard/preonic/keymap_midi.c +++ b/keyboard/preonic/keymap_midi.c @@ -20,37 +20,91 @@ along with this program. If not, see . #include uint8_t starting_note = 0x0C; +int offset = 7; void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) { - if (id != 0) { - if (record->event.pressed) { - midi_send_noteon(&midi_device, opt, (id & 0xFF), 127); - } else { - midi_send_noteoff(&midi_device, opt, (id & 0xFF), 127); - } - } - - if (record->event.key.col == 11 && record->event.key.row == 4 && record->event.pressed) { - starting_note++; - midi_send_cc(&midi_device, 0, 0x7B, 0); - midi_send_cc(&midi_device, 1, 0x7B, 0); - midi_send_cc(&midi_device, 2, 0x7B, 0); - midi_send_cc(&midi_device, 3, 0x7B, 0); - midi_send_cc(&midi_device, 4, 0x7B, 0); + if (id != 0) { + if (record->event.pressed) { + midi_send_noteon(&midi_device, opt, (id & 0xFF), 127); + } else { + midi_send_noteoff(&midi_device, opt, (id & 0xFF), 127); + } } - if (record->event.key.col == 8 && record->event.key.row == 4 && record->event.pressed) { - starting_note--; + + if (record->event.key.col == (MATRIX_COLS - 1) && record->event.key.row == (MATRIX_ROWS - 1)) { + if (record->event.pressed) { + starting_note++; + play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); + midi_send_cc(&midi_device, 0, 0x7B, 0); + midi_send_cc(&midi_device, 1, 0x7B, 0); + midi_send_cc(&midi_device, 2, 0x7B, 0); + midi_send_cc(&midi_device, 3, 0x7B, 0); + midi_send_cc(&midi_device, 4, 0x7B, 0); + return; + } else { + stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1))); + stop_all_notes(); + return; + } + } + if (record->event.key.col == (MATRIX_COLS - 2) && record->event.key.row == (MATRIX_ROWS - 1)) { + if (record->event.pressed) { + starting_note--; + play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); + midi_send_cc(&midi_device, 0, 0x7B, 0); + midi_send_cc(&midi_device, 1, 0x7B, 0); + midi_send_cc(&midi_device, 2, 0x7B, 0); + midi_send_cc(&midi_device, 3, 0x7B, 0); + midi_send_cc(&midi_device, 4, 0x7B, 0); + return; + } else { + stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[0 + offset])/12.0+(MATRIX_ROWS - 1))); + stop_all_notes(); + return; + } + } + + if (record->event.key.col == (MATRIX_COLS - 3) && record->event.key.row == (MATRIX_ROWS - 1) && record->event.pressed) { + offset++; midi_send_cc(&midi_device, 0, 0x7B, 0); midi_send_cc(&midi_device, 1, 0x7B, 0); midi_send_cc(&midi_device, 2, 0x7B, 0); midi_send_cc(&midi_device, 3, 0x7B, 0); midi_send_cc(&midi_device, 4, 0x7B, 0); + stop_all_notes(); + for (int i = 0; i <= 7; i++) { + play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); + _delay_us(80000); + stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1))); + _delay_us(8000); + } + return; + } + if (record->event.key.col == (MATRIX_COLS - 4) && record->event.key.row == (MATRIX_ROWS - 1) && record->event.pressed) { + offset--; + midi_send_cc(&midi_device, 0, 0x7B, 0); + midi_send_cc(&midi_device, 1, 0x7B, 0); + midi_send_cc(&midi_device, 2, 0x7B, 0); + midi_send_cc(&midi_device, 3, 0x7B, 0); + midi_send_cc(&midi_device, 4, 0x7B, 0); + stop_all_notes(); + for (int i = 0; i <= 7; i++) { + play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1)), 0xC); + _delay_us(80000); + stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[i + offset])/12.0+(MATRIX_ROWS - 1))); + _delay_us(8000); + } + return; } if (record->event.pressed) { - midi_send_noteon(&midi_device, record->event.key.row, starting_note + SCALE[record->event.key.col], 127); + // midi_send_noteon(&midi_device, record->event.key.row, starting_note + SCALE[record->event.key.col], 127); + midi_send_noteon(&midi_device, 0, (starting_note + SCALE[record->event.key.col + offset])+12*(MATRIX_ROWS - record->event.key.row), 127); + play_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row)), 0xF); } else { - midi_send_noteoff(&midi_device, record->event.key.row, starting_note + SCALE[record->event.key.col], 127); + // midi_send_noteoff(&midi_device, record->event.key.row, starting_note + SCALE[record->event.key.col], 127); + midi_send_noteoff(&midi_device, 0, (starting_note + SCALE[record->event.key.col + offset])+12*(MATRIX_ROWS - record->event.key.row), 127); + stop_note(((double)261.6)*pow(2.0, -1.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row))); } } \ No newline at end of file diff --git a/keyboard/preonic/keymap_midi.h b/keyboard/preonic/keymap_midi.h index 02bf35c243..c5917f884e 100644 --- a/keyboard/preonic/keymap_midi.h +++ b/keyboard/preonic/keymap_midi.h @@ -23,7 +23,11 @@ along with this program. If not, see . #define CHNL(note, channel) (note + (channel << 8)) -#define SCALE (int []){ 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28, 29, 31, 33, 35, 36} +#define SCALE (int []){ 0 + (12*0), 2 + (12*0), 4 + (12*0), 5 + (12*0), 7 + (12*0), 9 + (12*0), 11 + (12*0), \ + 0 + (12*1), 2 + (12*1), 4 + (12*1), 5 + (12*1), 7 + (12*1), 9 + (12*1), 11 + (12*1), \ + 0 + (12*2), 2 + (12*2), 4 + (12*2), 5 + (12*2), 7 + (12*2), 9 + (12*2), 11 + (12*2), \ + 0 + (12*3), 2 + (12*3), 4 + (12*3), 5 + (12*3), 7 + (12*3), 9 + (12*3), 11 + (12*3), \ + 0 + (12*4), 2 + (12*4), 4 + (12*4), 5 + (12*4), 7 + (12*4), 9 + (12*4), 11 + (12*4), } #define N_CN1 (0x600C + (12 * -1) + 0 ) #define N_CN1S (0x600C + (12 * -1) + 1 ) diff --git a/protocol/bluefruit.mk b/protocol/bluefruit.mk index 7e6328f6cb..473d73e012 100644 --- a/protocol/bluefruit.mk +++ b/protocol/bluefruit.mk @@ -3,7 +3,7 @@ PJRC_DIR = protocol/pjrc SRC += $(BLUEFRUIT_DIR)/main.c \ $(BLUEFRUIT_DIR)/bluefruit.c \ - serial_uart.c \ + ../serial_uart.c \ $(PJRC_DIR)/pjrc.c \ $(PJRC_DIR)/usb_keyboard.c \ $(PJRC_DIR)/usb_debug.c \ diff --git a/protocol/bluefruit/bluefruit.c b/protocol/bluefruit/bluefruit.c index cf26b83dff..47c63555cd 100644 --- a/protocol/bluefruit/bluefruit.c +++ b/protocol/bluefruit/bluefruit.c @@ -2,20 +2,16 @@ Bluefruit Protocol for TMK firmware Author: Benjamin Gould, 2013 Based on code Copyright 2011 Jun Wako - This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . - */ #include @@ -76,6 +72,13 @@ static void send_mouse(report_mouse_t *report); static void send_system(uint16_t data); static void send_consumer(uint16_t data); + +void sendString(char string[], int length) { + for(int i = 0; i < length; i++) { + serial_send(string[i]); + } +} + static host_driver_t driver = { keyboard_leds, send_keyboard, @@ -100,6 +103,7 @@ static void send_keyboard(report_keyboard_t *report) #endif bluefruit_serial_send(0xFD); for (uint8_t i = 0; i < KEYBOARD_REPORT_SIZE; i++) { + bluefruit_serial_send(report->raw[i]); } #ifdef BLUEFRUIT_TRACE_SERIAL @@ -198,5 +202,4 @@ static void send_consumer(uint16_t data) #ifdef BLUEFRUIT_TRACE_SERIAL bluefruit_trace_footer(); #endif -} - +} \ No newline at end of file diff --git a/protocol/bluefruit/bluefruit.h b/protocol/bluefruit/bluefruit.h index 4f9b58836d..ceacc4a367 100644 --- a/protocol/bluefruit/bluefruit.h +++ b/protocol/bluefruit/bluefruit.h @@ -2,17 +2,14 @@ Bluefruit Protocol for TMK firmware Author: Benjamin Gould, 2013 Based on code Copyright 2011 Jun Wako - This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along with this program. If not, see . */ @@ -25,4 +22,4 @@ along with this program. If not, see . host_driver_t *bluefruit_driver(void); -#endif +#endif \ No newline at end of file diff --git a/protocol/bluefruit/main.c b/protocol/bluefruit/main.c index 871062ab11..0dbb637e2c 100644 --- a/protocol/bluefruit/main.c +++ b/protocol/bluefruit/main.c @@ -22,7 +22,7 @@ along with this program. If not, see . #include #include #include -#include "serial.h" +#include "../serial.h" #include "keyboard.h" #include "usb.h" #include "host.h" @@ -40,23 +40,26 @@ along with this program. If not, see . #define BLUEFRUIT_HOST_DRIVER 1 #define PJRC_HOST_DRIVER 2 + int main(void) { CPU_PRESCALE(0); - DDRD = _BV(PD5); - DDRB = _BV(PB0); + // DDRD = _BV(PD5); + // DDRB = _BV(PB0); - PORTD = _BV(PD5); - PORTB = _BV(PB0); + // PORTD = _BV(PD5); + // PORTB = _BV(PB0); print_set_sendchar(sendchar); - usb_init(); - _delay_ms(2000); + // usb_init(); + // _delay_ms(2000); // while (!usb_configured()) /* wait */ + + dprintf("Initializing keyboard...\n"); keyboard_init(); @@ -64,53 +67,72 @@ int main(void) // is not configured, choose the Bluefruit, otherwise use USB // Definitely would prefer to have this driven by an input pin and make // it switch dynamically - BCG - if (!usb_configured()) { + // if (!usb_configured()) { - // Send power to Bluefruit... Adafruit says it takes 27 mA, I think - // the pins should provide 40 mA, but just in case I switch the - // Bluefruit using a transistor - BCG - DDRB = _BV(PB6); - PORTB |= _BV(PB6); + // // Send power to Bluefruit... Adafruit says it takes 27 mA, I think + // // the pins should provide 40 mA, but just in case I switch the + // // Bluefruit using a transistor - BCG + // DDRB = _BV(PB6); + // PORTB |= _BV(PB6); dprintf("Setting host driver to bluefruit...\n"); host_set_driver(bluefruit_driver()); dprintf("Initializing serial...\n"); serial_init(); - + + // char swpa[] = "+++\r\n"; + // for (int i = 0; i < 5; i++) { + // serial_send(swpa[i]); + // } + + // char ble_enable[] = "AT+BLEKEYBOARDEN=1\r\n"; + // for (int i = 0; i < 20; i++) { + // serial_send(ble_enable[i]); + // } + + // char reset[] = "ATZ\r\n"; + // for (int i = 0; i < 5; i++) { + // serial_send(reset[i]); + // } + + // for (int i = 0; i < 5; i++) { + // serial_send(swpa[i]); + // } + // wait an extra second for the PC's operating system // to load drivers and do whatever it does to actually // be ready for input _delay_ms(1000); - PORTD = ~_BV(PD5); + // PORTD = ~_BV(PD5); dprintf("Starting main loop"); while (1) { keyboard_task(); } - } else { +// } else { - // I'm not smart enough to get this done with LUFA - BCG - dprintf("Setting host driver to PJRC...\n"); - host_set_driver(pjrc_driver()); -#ifdef SLEEP_LED_ENABLE - sleep_led_init(); -#endif - // wait an extra second for the PC's operating system - // to load drivers and do whatever it does to actually - // be ready for input - _delay_ms(1000); - PORTB = ~_BV(PB0); - dprintf("Starting main loop"); - while (1) { - while (suspend) { - suspend_power_down(); - if (remote_wakeup && suspend_wakeup_condition()) { - usb_remote_wakeup(); - } - } - keyboard_task(); - } - } +// // I'm not smart enough to get this done with LUFA - BCG +// dprintf("Setting host driver to PJRC...\n"); +// host_set_driver(pjrc_driver()); +// #ifdef SLEEP_LED_ENABLE +// sleep_led_init(); +// #endif +// // wait an extra second for the PC's operating system +// // to load drivers and do whatever it does to actually +// // be ready for input +// _delay_ms(1000); +// PORTB = ~_BV(PB0); +// dprintf("Starting main loop"); +// while (1) { +// while (suspend) { +// suspend_power_down(); +// if (remote_wakeup && suspend_wakeup_condition()) { +// usb_remote_wakeup(); +// } +// } +// keyboard_task(); +// } +// } } diff --git a/protocol/lufa/descriptor.c b/protocol/lufa/descriptor.c index 5454926b18..b345da27b4 100644 --- a/protocol/lufa/descriptor.c +++ b/protocol/lufa/descriptor.c @@ -266,7 +266,7 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .ConfigAttributes = (USB_CONFIG_ATTR_RESERVED | USB_CONFIG_ATTR_REMOTEWAKEUP), - .MaxPowerConsumption = USB_CONFIG_POWER_MA(100) + .MaxPowerConsumption = USB_CONFIG_POWER_MA(500) }, /* diff --git a/protocol/lufa/lufa.c b/protocol/lufa/lufa.c index b31f3031ee..85c7bf712f 100644 --- a/protocol/lufa/lufa.c +++ b/protocol/lufa/lufa.c @@ -840,13 +840,13 @@ int main(void) /* wait for USB startup & debug output */ - // while (USB_DeviceState != DEVICE_STATE_Configured) { + while (USB_DeviceState != DEVICE_STATE_Configured) { // #if defined(INTERRUPT_CONTROL_ENDPOINT) // ; // #else USB_USBTask(); // #endif - // } + } print("USB configured.\n"); /* init modules */