diff --git a/Firmware/Keyboard/Config.h b/Firmware/Keyboard/Config.h index 9d686ba..47daf82 100644 --- a/Firmware/Keyboard/Config.h +++ b/Firmware/Keyboard/Config.h @@ -7,9 +7,6 @@ // For ease of code sharing with the OsuPad #define SWITCH_COUNT 7 -// essentially sizeof the config type + 1 for some reason -// TODO: what is the reason -#define CONFIG_BYTES (SWITCH_COUNT + 4) #define MAGIC_RESET_NUMBER 42 #define FIRMWARE_VERSION 1 @@ -19,8 +16,14 @@ typedef struct { bool ledsOn; uint8_t debounce; uint8_t version; + // used to reboot into programming mode + uint8_t reboot; } sdvx_config_t; +// + 1 for some reason +// TODO: what is the reason +#define CONFIG_BYTES sizeof(sdvx_config_t) + extern sdvx_config_t sdvxConfig; extern void InitConfig(void); diff --git a/Firmware/Keyboard/DFU/hid_bootloader_loader.py b/Firmware/Keyboard/DFU/hid_bootloader_loader.py index 2598fde..686be55 100644 --- a/Firmware/Keyboard/DFU/hid_bootloader_loader.py +++ b/Firmware/Keyboard/DFU/hid_bootloader_loader.py @@ -22,6 +22,7 @@ import sys from pywinusb import hid from intelhex import IntelHex +import time # Device information table @@ -50,6 +51,25 @@ def get_hid_device_handle(): else: return valid_hid_devices[0] +def bootloader_boot(): + hid_device_filter = hid.HidDeviceFilter(vendor_id=0x16D0, + product_id=0x0A6D, + product_name="Pocket Voltex Config") + + valid_hid_devices = hid_device_filter.get_devices() + + if len(valid_hid_devices) is 0: + return False + else: + try: + hid_device = valid_hid_devices[0] + hid_device.open() + output_report_data = [0,0,0,0,0,0,0,0,0,0,0,42] + + hid_device.send_output_report(output_report_data) + finally: + hid_device.close() + return True def send_page_data(hid_device, address, data): # Bootloader page data should be the HID Report ID (always zero) followed @@ -66,8 +86,15 @@ def program_device(hex_data, device_info): hid_device = get_hid_device_handle() if hid_device is None: - print("No valid HID device found.") - sys.exit(1) + if not bootloader_boot(): + print("No valid HID device found.") + sys.exit(1) + print("Rebooted to bootloader") + time.sleep(6) + hid_device = get_hid_device_handle() + if hid_device is None: + print("No valid HID device found.") + sys.exit(1) try: hid_device.open() diff --git a/Firmware/Keyboard/Descriptors.c b/Firmware/Keyboard/Descriptors.c index 95dcfe9..cf5f9e5 100644 --- a/Firmware/Keyboard/Descriptors.c +++ b/Firmware/Keyboard/Descriptors.c @@ -38,26 +38,27 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM LEDReport[] = { HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ HID_RI_USAGE(8, 0x00), /* Undefined */ - HID_RI_COLLECTION(8, 0x01), /* Application */ - // Global Items + HID_RI_COLLECTION(8, 0x01), /* Application */ + // Globals HID_RI_REPORT_COUNT(8, LED_TOTAL_COUNT), HID_RI_REPORT_SIZE(8, 8), HID_RI_LOGICAL_MINIMUM(8, 0), HID_RI_LOGICAL_MAXIMUM(8, BRIGHTNESS_LEVELS-1), HID_RI_USAGE_PAGE(8, 0x0A), // Ordinals - // Locals repeated for INPUT/OUTPUT + // Locals 0x79, STRING_ID_LED_INDIV, //HID_RI_STRING_MINIMUM(8, STRING_ID_LED_INDIV), 0x89, STRING_ID_LED_INDIV + LED_TOTAL_COUNT, //HID_RI_STRING_MAXIMUM(8, STRING_ID_LED_INDIV + LED_COUNT), HID_RI_USAGE_MINIMUM(8, 1), // LED 1 - HID_RI_USAGE_MAXIMUM(8, LED_TOTAL_COUNT), // LED 8 + HID_RI_USAGE_MAXIMUM(8, LED_TOTAL_COUNT), // LED 8 + buttons HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE), // INPUT is also needed to be recognised by Bemanitools + //HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE), 0x79, STRING_ID_LED_INDIV, //HID_RI_STRING_MINIMUM(8, STRING_ID_LED_INDIV), 0x89, STRING_ID_LED_INDIV + LED_TOTAL_COUNT, //HID_RI_STRING_MAXIMUM(8, STRING_ID_LED_INDIV + LED_COUNT), HID_RI_USAGE_MINIMUM(8, 1), // LED 1 - HID_RI_USAGE_MAXIMUM(8, LED_TOTAL_COUNT), // LED 8 + HID_RI_USAGE_MAXIMUM(8, LED_TOTAL_COUNT), // LED 8 + buttons HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE), HID_RI_END_COLLECTION(0), }; diff --git a/Firmware/Keyboard/Descriptors.h b/Firmware/Keyboard/Descriptors.h index 375809e..fe4ef95 100644 --- a/Firmware/Keyboard/Descriptors.h +++ b/Firmware/Keyboard/Descriptors.h @@ -110,7 +110,7 @@ #define LED_EPADDR (ENDPOINT_DIR_IN | 4) - #define KEYBOARD_EPSIZE 8 + #define KEYBOARD_EPSIZE SWITCH_COUNT + 2 #define MOUSE_EPSIZE 8 #define GENERIC_EPSIZE CONFIG_BYTES #define LED_EPSIZE LED_TOTAL_COUNT diff --git a/Firmware/Keyboard/FollowerGen.py b/Firmware/Keyboard/FollowerGen.py new file mode 100644 index 0000000..2c95000 --- /dev/null +++ b/Firmware/Keyboard/FollowerGen.py @@ -0,0 +1,29 @@ +BRIGHTNESS_LEVELS = 64 +STAGES = 3 +LEDS = 8 +RING = [0,2,4,6,7,5,3,1] + +TOTAL = STAGES * BRIGHTNESS_LEVELS +INTERVAL = TOTAL / LEDS + +RISE = range(0,BRIGHTNESS_LEVELS) +FALL = range(BRIGHTNESS_LEVELS-1,-1,-1) +OFF = [0]*BRIGHTNESS_LEVELS + +R = FALL + OFF + RISE +Rdir = [0, 0, 1] +G = RISE + FALL + OFF +Gdir = [1, 0, 0] +B = OFF + RISE + FALL +Bdir = [0, 1, 0] + +print "// Generated by FollowerGen.py" +print "static const uint8_t followStart[][4] = {" +for i in range(LEDS): + # map to the different order in the board + actualIndex = RING.index(i) + offset = INTERVAL * actualIndex + dirOffset = offset / BRIGHTNESS_LEVELS + dir = Rdir[dirOffset] | Gdir[dirOffset] << 1 | Bdir[dirOffset] << 2 + print "\t{%d, %d, %d, %s}," % (R[offset], G[offset], B[offset],'{:#05b}'.format(dir)) +print "};" diff --git a/Firmware/Keyboard/Keyboard.c b/Firmware/Keyboard/Keyboard.c index 4b3efad..491f2f8 100644 --- a/Firmware/Keyboard/Keyboard.c +++ b/Firmware/Keyboard/Keyboard.c @@ -2,8 +2,15 @@ #include "Config.h" #include "Encoder.h" #include "LED.h" +#include "LEDPatterns.h" #define READ_SWITCH(x) (!(*pins[switches[x].switchPort] & _BV(switches[x].switchPin))) +#define MAGIC_BOOT_KEY 0xDEADBE7A +// offset * word size +#define BOOTLOADER_START_ADDRESS (0x1c00 * 2) + +// How long to wait before moving to internal lighting +#define HID_LED_TIMEOUT 5000 typedef struct { @@ -127,10 +134,10 @@ static switch_t switches[SWITCH_COUNT] = { }; static uint8_t switchesChanged = 1; +// Set to max already so we have our init flash +static uint16_t hidTimeout = HID_LED_TIMEOUT; + uint32_t Boot_Key ATTR_NO_INIT; -#define MAGIC_BOOT_KEY 0xDEADBE7A -// offset * word size -#define BOOTLOADER_START_ADDRESS (0x1c00 * 2) void Bootloader_Jump_Check(void) ATTR_INIT_SECTION(3); void Bootloader_Jump_Check(void) @@ -178,25 +185,7 @@ int main(void) GlobalInterruptEnable(); // Blink to show we're not in bootloader - for(uint8_t flash = 0; flash < BRIGHTNESS_LEVELS; flash++) { - for(uint8_t i = 0; i < LED_COUNT; i++) { - uint8_t bright = ledLogCurve[flash]; - led_set(i, bright, bright/2, 0); - _delay_us(500); - } - } - // NOTE: signed - for(int8_t flash = BRIGHTNESS_LEVELS; flash >= 0; flash--) { - for(uint8_t i = 0; i < LED_COUNT; i++) { - uint8_t bright = ledLogCurve[flash]; - led_set(i, bright, bright/2, 0); - _delay_us(500); - } - } - - - // Not in SetupHardware so we don't time out - USB_Init(); + led_anim_flash(); for (;;) { @@ -235,6 +224,8 @@ void SetupHardware() /* Hardware Initialization */ encoder_init(); led_init(); + + USB_Init(); } /** HID class driver callback function for the creation of HID reports to the host. @@ -287,6 +278,8 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn MouseReport->X = encoder_get(0); MouseReport->Y = -encoder_get(1); + led_knobs_update(MouseReport->X, MouseReport->Y); + encoder_set(0, 0); encoder_set(1, 0); @@ -297,12 +290,12 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn memcpy(ConfigReport, &sdvxConfig, sizeof(sdvx_config_t)); *ReportSize = CONFIG_BYTES; return true; - } else if(HIDInterfaceInfo == &LED_HID_Interface) { + }/* else if(HIDInterfaceInfo == &LED_HID_Interface) { uint8_t* LEDReport = (uint8_t*)ReportData; memcpy(LEDReport, (uint8_t*)leds, LED_TOTAL_COUNT); *ReportSize = LED_TOTAL_COUNT; return true; - } + }*/ *ReportSize = 0; return false; @@ -329,6 +322,9 @@ void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDI } SetConfig(ConfigReport); } else if(HIDInterfaceInfo == &LED_HID_Interface && ReportType == HID_REPORT_ITEM_Out) { + // reset our timeout + hidTimeout = 0; + LED_Report_t* LEDReport = (LED_Report_t*)ReportData; memcpy((uint8_t*)leds, LEDReport->mainLights, LED_RAW_COUNT); @@ -384,10 +380,16 @@ void EVENT_USB_Device_ControlRequest(void) /** Event handler for the USB device Start Of Frame event. */ void EVENT_USB_Device_StartOfFrame(void) { - HID_Device_MillisecondElapsed(&Keyboard_HID_Interface); + HID_Device_MillisecondElapsed(&Keyboard_HID_Interface); HID_Device_MillisecondElapsed(&Mouse_HID_Interface); - HID_Device_MillisecondElapsed(&Generic_HID_Interface); - HID_Device_MillisecondElapsed(&LED_HID_Interface); + HID_Device_MillisecondElapsed(&Generic_HID_Interface); + HID_Device_MillisecondElapsed(&LED_HID_Interface); + + if(hidTimeout > HID_LED_TIMEOUT) { + led_frame(); + } else { + hidTimeout++; + } for(int i = 0; i < SWITCH_COUNT; i++) { if(switches[i].debounce) { diff --git a/Firmware/Keyboard/LED.c b/Firmware/Keyboard/LED.c index c95cbaf..d1eda7b 100644 --- a/Firmware/Keyboard/LED.c +++ b/Firmware/Keyboard/LED.c @@ -15,6 +15,10 @@ #define LED_DDR DDRB #define LED_MASK (0b111111 << 2) +#define UPDATE_HZ 50 +// prescaler is the div8 +#define TIMER_COMPARE ((F_CPU / 8 / UPDATE_HZ / GND_COUNT / BRIGHTNESS_LEVELS)-1) + #define R 2 #define G 1 #define B 0 @@ -38,10 +42,7 @@ void led_init() { TCCR0A = _BV(WGM01); // clk/8 prescaler TCCR0B = _BV(CS01); - // 0.16% error on above calculation - //OCR0A = 64; // 60Hz - OCR0A = 77; // 50Hz - //OCR0A = 48; // 80Hz + OCR0A = TIMER_COMPARE; // Enable interrupt on OCR0A TIMSK0 = _BV(OCIE0A); // Clear interrupt @@ -55,6 +56,22 @@ void led_set(uint8_t num, uint8_t r, uint8_t g, uint8_t b) { leds[offset+B] = b; } +void led_set_max(uint8_t num, uint8_t r, uint8_t g, uint8_t b) { + uint8_t offset = num * 3; + if(r > leds[offset+R]) + leds[offset+R] = r; + if(g > leds[offset+G]) + leds[offset+G] = g; + if(b > leds[offset+B]) + leds[offset+B] = b; +} + +void led_set_all(uint8_t r, uint8_t g, uint8_t b) { + for(uint8_t i = 0; i < LED_COUNT; i++) { + led_set(i, r, g, b); + } +} + void led_set_indiv(uint8_t num, uint8_t val) { leds[num] = val; } @@ -115,8 +132,7 @@ ISR(TIMER0_COMPA_vect) { // Turn off before switch LED_PORT &= ~LED_MASK; - GND_DDR &= ~GND_MASK; // Enable new ground - GND_DDR |= currentGndMask; + GND_DDR = (GND_DDR & ~GND_MASK) | currentGndMask; LED_PORT |= out; } \ No newline at end of file diff --git a/Firmware/Keyboard/LED.h b/Firmware/Keyboard/LED.h index dfeddd6..6c66ccb 100644 --- a/Firmware/Keyboard/LED.h +++ b/Firmware/Keyboard/LED.h @@ -13,8 +13,6 @@ #define LED_TOTAL_COUNT (LED_RAW_COUNT + LED_VIRTUAL_COUNT) #define BRIGHTNESS_LEVELS 64 -const static uint8_t ledLogCurve[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 17, 20, 23, 25, 28, 31, 34, 37, 40, 43, 45, 48, 50, 52, 53, 55, 56, 57, 58, 59, 60, 60, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63}; - // Arranged left to right, top to bottom // LED order is BGR...BGR volatile uint8_t leds[LED_RAW_COUNT]; @@ -23,8 +21,23 @@ volatile uint8_t leds[LED_RAW_COUNT]; // Order is BT-1-4, FX-L, FX-R static const uint8_t ledMap[] = {2, 0, 1, 3, 6, 7}; +// map LEDs to run in a circle +static const uint8_t ledCircleMap[] = {0, 2, 4, 6, 7, 5, 3, 1}; + void led_init(void); void led_set(uint8_t num, uint8_t r, uint8_t g, uint8_t b); +void led_set_max(uint8_t num, uint8_t r, uint8_t g, uint8_t b); +void led_set_all(uint8_t r, uint8_t g, uint8_t b); void led_set_indiv(uint8_t num, uint8_t val); +const static uint8_t ledLogCurve[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 2, 2, + 3, 3, 4, 5, 6, 7, 8, 9, + 11, 13, 15, 17, 20, 23, 25, 28, + 31, 34, 37, 40, 43, 45, 48, 50, + 52, 53, 55, 56, 57, 58, 59, 60, + 60, 61, 61, 61, 62, 62, 62, 62, + 63, 63, 63, 63, 63, 63, 63, 63, 63}; + #endif diff --git a/Firmware/Keyboard/LEDPatterns.c b/Firmware/Keyboard/LEDPatterns.c new file mode 100644 index 0000000..2978306 --- /dev/null +++ b/Firmware/Keyboard/LEDPatterns.c @@ -0,0 +1,171 @@ +#include "LEDPatterns.h" + +static enum LEDMode animMode = NONE; +static uint8_t frameCounter = 0; + +// various things for the different modes +static int8_t flash; +static int8_t flashDirection; + +typedef struct { + uint8_t value[3]; + uint8_t direction; +} RGBFader; + +static RGBFader followers[LED_COUNT]; + +typedef struct { + const uint8_t rgb[3]; + uint8_t leds[2]; + uint8_t levels[2]; +} KnobLights; + +static KnobLights knobs[2] = { + // Aqua, top left LEDs + {{0,1,1}, {0,1}, {BRIGHTNESS_LEVELS-1, 0}}, + // Pink, top right LEDs + {{1,0,1}, {6,7}, {0, BRIGHTNESS_LEVELS-1}} +}; + +// Called every 1ms +void led_frame(void) { + if(++frameCounter < LED_MS_PER_FRAME) { + return; + } + + frameCounter = 0; + + uint8_t followDir; + RGBFader* follow; + + switch(animMode) { + case INIT_FLASH: + if(flashDirection) { + flash+=3; + if(flash >= BRIGHTNESS_LEVELS - 1) { + flash = BRIGHTNESS_LEVELS - 1; + flashDirection = 0; + } + } else { + flash-=3; + if(flash <= 0) { + //animMode = NONE; + led_anim_follower(); + flash = 0; + } + } + + uint8_t bright = ledLogCurve[flash]; + led_set_all(bright, bright/2, 0); + break; + case FOLLOWER: + /*for(uint8_t led = 0; led < LED_COUNT; led++) { + follow = followers + led; + followDir = follow->direction; + for(uint8_t i = 0; i < 3; i++) { + if(followDir & 1) { + if(follow->value[i]++ >= BRIGHTNESS_LEVELS - 1) { + follow->value[i] = BRIGHTNESS_LEVELS - 1; + follow->direction <<= 1; + if(follow->direction == 0b1000) { + follow->direction = 0b001; + } + } + } else { + if(follow->value[i] > 0) { + follow->value[i]--; + } + } + followDir >>= 1; + } + led_set(led, follow->value[0], follow->value[1], follow->value[2]); + }*/ + break; + case BREATHE: + default: + break; + } + + led_set_all(0,0,0); + for(uint8_t i = 0; i < 2; i++) { + for(uint8_t led = 0; led < 2; led++) { + uint8_t r = knobs[i].levels[led] * knobs[i].rgb[0]; + uint8_t g = knobs[i].levels[led] * knobs[i].rgb[1]; + uint8_t b = knobs[i].levels[led] * knobs[i].rgb[2]; + led_set_max(ledCircleMap[knobs[i].leds[led]], r, g, b); + } + } +} + +void led_knob_indiv(KnobLights* knob, int8_t value) { + /* This is annoying and repetitive but don't think there's a nicer way */ + if(value > 0) { + knob->levels[0] += value; + if(value > knob->levels[1]) { + // Not properly doing rollover since the knob doesn't spin that fast + knob->levels[1] = 0; + } else { + knob->levels[1] -= value; + } + + if(knob->levels[0] > BRIGHTNESS_LEVELS) { + knob->levels[0] = 0; + knob->levels[1] = BRIGHTNESS_LEVELS - 1; + knob->leds[1] = knob->leds[0]; + if(knob->leds[0] > 0) { + knob->leds[0]--; + } else { + knob->leds[0] = LED_COUNT - 1; + } + } + } else if(value < 0) { + if(-value > knob->levels[0]) { + knob->levels[0] = 0; + } else { + knob->levels[0] += value; + } + knob->levels[1] -= value; + + if(knob->levels[1] > BRIGHTNESS_LEVELS) { + knob->levels[0] = BRIGHTNESS_LEVELS - 1; + knob->levels[1] = 0; + knob->leds[0] = knob->leds[1]; + knob->leds[1]++; + knob->leds[1] %= LED_COUNT; + } + } +} + +void led_knobs_update(int8_t left, int8_t right) { + led_knob_indiv(&knobs[0], left*2); + led_knob_indiv(&knobs[1], right*2); +} + +void led_anim_flash(void) { + animMode = INIT_FLASH; + // starting at 2 stops us getting a frame of red + flash = 2; + flashDirection = 1; +} + +void led_anim_follower(void) { + // Generated by FollowerGen.py + static const uint8_t followStart[][4] = { + {63, 0, 0, 0b010}, + {40, 0, 23, 0b001}, + {39, 24, 0, 0b010}, + {16, 0, 47, 0b001}, + {15, 48, 0, 0b010}, + {0, 7, 56, 0b100}, + {0, 55, 8, 0b100}, + {0, 31, 32, 0b100}, + }; + animMode = FOLLOWER; + + for(uint8_t i = 0; i < LED_COUNT; i++) { + for(uint8_t j = 0; j < 3; j++) { + followers[i].value[j] = followStart[i][j]; + } + followers[i].direction = followStart[i][3]; + } +} \ No newline at end of file diff --git a/Firmware/Keyboard/LEDPatterns.h b/Firmware/Keyboard/LEDPatterns.h new file mode 100644 index 0000000..dd870c4 --- /dev/null +++ b/Firmware/Keyboard/LEDPatterns.h @@ -0,0 +1,22 @@ +#ifndef _LED_PATTERNS_H +#define _LED_PATTERNS_H + +#include +#include "LED.h" + +#define LED_FRAMERATE 30 +#define LED_MS_PER_FRAME (1000 / LED_FRAMERATE) + +enum LEDMode { + NONE = 0, + INIT_FLASH, + FOLLOWER, + BREATHE, +}; + +void led_frame(void); +void led_anim_flash(void); +void led_anim_follower(void); +void led_knobs_update(int8_t left, int8_t right); + +#endif \ No newline at end of file diff --git a/Firmware/Keyboard/makefile b/Firmware/Keyboard/makefile index 5a7300b..76a2991 100644 --- a/Firmware/Keyboard/makefile +++ b/Firmware/Keyboard/makefile @@ -18,7 +18,7 @@ F_CPU = 8000000 F_USB = $(F_CPU) OPTIMIZATION = s TARGET = Keyboard -SRC = $(TARGET).c Descriptors.c Config.c Encoder.c LED.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) +SRC = $(TARGET).c Descriptors.c Config.c Encoder.c LED.c LEDPatterns.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS) LUFA_PATH = ../LUFA CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -IConfig/ LD_FLAGS = @@ -65,5 +65,4 @@ flashboot: $(AVRDUDE) -U flash:w:DFU/BootloaderHID.hex:i flash: all - sleep 1 python DFU/hid_bootloader_loader.py atmega16u2 Keyboard.hex