From 8e21ab8b88a94f2339610d27c2f57908ed1ff089 Mon Sep 17 00:00:00 2001 From: Bottersnike Date: Thu, 15 Aug 2024 14:54:01 +0100 Subject: [PATCH] Best commit message yet --- .vscode/settings.json | 3 +- Makefile | 3 +- NUC123/NUC123.ld | 4 +- README.md | 20 ++- docs/Colours.md | 11 -- docs/Development.md | 24 ++++ docs/Protocol_LED.md | 230 +++++++++++++++++++++++++++++++ docs/Protocol_PSoC.md | 22 +++ docs/Protocols.md | 2 + generic.mk | 2 +- src/descriptors.c | 226 +++++++++++++++++++------------ src/fmc.c | 22 ++- src/fmc_user.h | 9 +- src/hid.c | 136 +++++++++++++++++++ src/hid_def.h | 35 +++++ src/io4.c | 10 -- src/led.c | 22 +-- src/led.h | 10 +- src/led_impl.c | 308 ++++++++++++++++++++++++++++++------------ src/led_shared.h | 38 +++--- src/main.c | 15 +- src/pins.h | 4 +- src/psoc.c | 27 ++-- src/psoc.h | 4 +- src/slider.c | 14 +- src/slider.h | 2 +- src/sys.c | 35 ++++- src/tasoller.h | 3 + src/timestamp.c | 2 + src/ui.c | 122 +++++++++-------- src/usb_def.h | 33 +++-- src/usb_inc/hid.h | 6 + src/usbd_driver.c | 4 +- 33 files changed, 1058 insertions(+), 350 deletions(-) delete mode 100644 docs/Colours.md create mode 100644 docs/Protocol_LED.md create mode 100644 docs/Protocol_PSoC.md create mode 100644 docs/Protocols.md create mode 100644 src/timestamp.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 8a870d4..816ccd7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -71,5 +71,6 @@ "bold": false, "italic": false } - ] + ], + "python.analysis.typeCheckingMode": "basic" } diff --git a/Makefile b/Makefile index 0f469de..afec784 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ SRC_DIR := src OBJ_DIR := obj BINARY_NAME := host_aprom -OPTIM := -O0 +# OPTIM := -O0 +OPTIM := -O1 # OPTIM := -Os -flto include GCC.mk LIBRARY_MODULES := clk uart timer i2c diff --git a/NUC123/NUC123.ld b/NUC123/NUC123.ld index 5fa8485..626715c 100644 --- a/NUC123/NUC123.ld +++ b/NUC123/NUC123.ld @@ -27,7 +27,9 @@ ENTRY(Reset_Handler) SECTIONS { .text : { KEEP(*(.vectors)) - . = . + 16; + KEEP(*(.compile_timestamp)) + . = ALIGN(16); + /* KEEP(*(.init)) */ /* KEEP(*(.text._entry)) */ *(.text*) diff --git a/README.md b/README.md index b5699bb..40cbb27 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,22 @@ See [Development](./Development.md) for development information. -Yes, the code is a mess. I just wanted to get something onto git y'know :). +Like what you see? [Buy me a coffee](https://ko-fi.com/bottersnike) + +## Features +- Superior slider processing; no more dropped holds or missed notes! +- Native arcade IO support +- Air sensor anti-jitter (fixes dodgy sensors) +- Intuitive menus +- Arcade-accurate colours + +And of course, it's all open source :). ## Setup ### Installing Firmware -- Disconnect the PC cable from the TASOLLER -- Hold down the FN2 button, while reconnecting the PC cable -- `HOSTMCU` should show as a device -- Use `Update V1.1.exe` from official firmware updates to load the firmware - -Currently, Dao CFW is required on the LED board. This is controlled by `LED_FIRMWARE_CFW` in `src/led.h`. +- Download both `host_aprom.bin` and `led_aprom.bin` +- Run `TASOLLER-FirmwareUpdater.exe` +- Follow the instructions ### Configuring segatools This firmware emulates arcade IO. As such, segatools' emulation should be disabled, by adding the following lines: diff --git a/docs/Colours.md b/docs/Colours.md deleted file mode 100644 index 4a06a8c..0000000 --- a/docs/Colours.md +++ /dev/null @@ -1,11 +0,0 @@ -| | RGB | Approx HSV | -| ----------- | ------- | ----------- | -| Light gates | #910c45 | 334,92 ,57 | -| Cells: | #FEFE00 | 60 ,100,100 | -| Dividers | #FE00FE | 300,100,100 | - - -Something: FF 00 8B (FF) [Magenta] -Something: 30 0A 60 (FF) [Purple?] -Something: 2D 0A 5A (FF) -Something: 2B 0A 55 (FF) diff --git a/docs/Development.md b/docs/Development.md index 5a2aacf..af04131 100644 --- a/docs/Development.md +++ b/docs/Development.md @@ -71,6 +71,30 @@ Additionally, the following tests may be run for diagnostics: xHSETT can be used to directly control the USB device, such as sending SUSPEND signals. +### Endpoint mapping +The NUC123 has 8 endpoints available. They are currently allocated as: + +| Endpoint | Use | Direction | Buffer size | +| -------- | --------------- | --------- | ----------- | +| 0 | Control | IN | 64 | +| 1 | Control | OUT | 64 | +| 2 | Slider CDC Data | IN | 64 | +| 3 | Slider CDC Data | OUT | 64 | +| 4 | CDC Command | IN | 16 | +| 5 | IO4 HID | IN | 64 | +| 6 | Misc HID | IN | 64 | +| 7 | Misc HID | OUT | 64 | + +The USB PHY has a 512 byte memory buffer. 16 bytes of this are reserved for setup packets. We currently use 464 bytes for endpoints, leaving 32 bytes unassigned. + +amdaemon rejects HID packets with unknown report IDs, so endpoint 5 and 6 cannot be merged. + +Endpoint 7 is currently unused, and could be bundled into endpoint 1 (as IO4 is currently doing). + +To add CDC serial for the air towers for we would need 4 additional endpoints, which we don't have. + +We can't use the NUC121 because it's muxed with the 123 :). + ## Airs See https://www.shinkoh-elecs.jp/wp-content/uploads/2024/05/C_KB1281_1581_24A.pdf diff --git a/docs/Protocol_LED.md b/docs/Protocol_LED.md new file mode 100644 index 0000000..5387111 --- /dev/null +++ b/docs/Protocol_LED.md @@ -0,0 +1,230 @@ +# LED microcontroller communication protocol +The Host and LED controller are connected with a pair of wires that are on I2C-capable pins for each. They do not exclusively use I2C. + +During the LED bootloader, PA10 and PA11 are read as digital inputs. If both are low, the LED bootloader enters programming mode. When the Host firmware starts, it reads FN2. If the button is depressed, the two lines are pulled low. Otherwise the Host begins operation as an I2C slave, pulling both lines high. + +The Host operates as the slave, as the LED microcontroller writing to the LEDs is a timing-critical that cannot be interrupted. + +## General comments +The TASOLLER has 48 LEDs on the ground slider (on two 24-LED PCBs) and 24 LEDs in each tower. + +The 48 ground LEDs, while individually controllable by the LED firmware, are not exposed to the Host by this protocol. Instead, 31 "logical" LEDs are exposed. The 48th LED is obscured by the housing of the controller, and as such is always left unlit. Each cell has two LEDs and each divider has one, giving 16*2+15 = 31. + +All RGB and HSV colours are transmitted as in the following structures: + +```c +typedef struct grb { + uint8_t g; + uint8_t r; + uint8_t b; +} grb_t; + +typedef struct hsv { + uint16_t h; + uint8_t s; + uint8_t v; +} hsv_t; +``` + +## Stock protocol +In the stock protocol, the Host exposes 256 single-byte registers. The LED microcontroller reads register 0 to get the command byte, then subsequently reads the appropriate number of registers for the given command. + +Stock firmware implements two commands. `A5` is a basic LED write based on a number of parameters. `5A` writes raw RGB data to all LEDs. Factory-stock and mainland "custom" firmware implement slight variants of both. + +### Basic write (factory) [`A5`] +```c +{ + uint8_t u8Cmd = 0xA5; + uint32_t u32Ground; + uint8_t u8TowerFill; + + uint8_t u8HueLeft; + uint8_t u8HueRight; + uint8_t u8HueGround; + uint8_t u8HueGroundActive; + + struct { + uint8_t bTowersOff: 1; + uint8_t bGroundOff: 1; + uint8_t bSeparators: 2; + uint8_t Rsv: 1; + uint8_t bKeyMode: 3; + }; + uint8_t u8LedSpecial; +} +``` + +| Parameter | Value | +| ------------------- | ---------------------------------------------------------- | +| `u32Ground` | 32 bits indicating boolean state of every pad | +| `u8TowerFill` | Bits 0 through 5 indicate the state of the air sensors | +| `u8HueLeft` | Hue of the left tower | +| `u8HueRight` | Hue of the right tower | +| `u8HueGround` | Hue of the slider cells | +| `u8HueGroundActive` | Hue of the slider cells when active, and dividers | +| `bTowersOff` | Disable tower LEDs | +| `bGroundOff` | Disable ground LEDs | +| `bSeparators` | Number of separating dividers | +| `bKeyMode` | How to group cells when lighting them based on `u32Ground` | +| `u8LedSpecial` | Performs specific functions as described below | + +`bSeparators` enumeration: +- `0`: Divider every 4 cells (creates 4 sections) +- `1`: Divider every 2 cells (creates 8 sections) +- `2`: Divider every 1 cell (creates 16 sections) +- `3`: No dividers + +`u8LedSpecial` enumeration: +- `0x00`: Normal operation (all other values ignore ground data) +- `0x01`: [Stock] Uses colours. Bar 1 full (from right) +- `0x02`: [Stock] Uses colours. Bar 2 full (from right) +- `0x03`: [Stock] Uses colours. Bar 3 full (from right) +- `0x04`: [Stock] Uses colours. Bar 4 full (from right) +- `0x1X`: [Stock] All white with black gaps. X bars black (from right) +- `0x80`: [Stock] Flashes three times + +All hues are divided by 5, such that 360° = `72` + +### Basic write (mainland) [`A5`] +```c +{ + uint8_t u8Cmd = 0xA5; + uint32_t u32Ground; + uint8_t u8TowerFill; + + struct { + uint8_t bInvertRainbow: 1; + uint8_t bRainbowSeparator: 7; + }; + + uint8_t Rsv07[3]; + + struct { + bTowersOff: 1; + bGroundOff: 1; + Rsv: 6; + }; + uint8_t u8LedSpecial; +} +``` + +| Parameter | Value | +| ------------------- | ---------------------------------------------- | +| `bDisplayRainbow ` | Displays a rainbow across the ground LEDs | +| `bRainbowSeparator` | The number of separator LEDs to light | +| `u8LedSpecial` | Performs specific functions as described below | + +Other parameters function as in factory firmware. + +`bRainbowSeparator` will light separators from 1 through (x+1). Maximum value 14. + +`u8LedSpecial` enumeration: +- `0x00`: Normal operation (all other values ignore ground data) +- `0x1X`: Rainbow with black gaps. X bars black (from right) +- `0x20`: Flashes three times + +### RGB write (factory) [`5A`] +```c +struct { + uint8_t u8Cmd = 0x5A; + + grb_t aGround[31]; + grb_t aTowerLeft[24]; + grb_t aTowerRight[24]; +} +``` + +Writes raw RGB data to all LEDs. Colours are transmitted in GRB format. + +### RGB write (mainland) [`5A`] +```c +struct { + uint8_t u8Cmd = 0x5A; + struct { + bTowersOff: 1; + bGroundOff: 1; + u8LedSpecial: 6; + } u8Config; + + grb_t aGround[31]; + grb_t aTowerLeft[24]; + grb_t aTowerRight[24]; +} +``` + +Writes raw RGB data to all LEDs. Colours are transmitted in GRB format. + +See "Basic write (mainland)" for documentation of `u8LedSpecial`. + +## Custom firmware protocol +In our custom protocol packets larger than 256 bytes need transmitted. To retain compatibility with the stock protocol, the Host still only exposes registers with an 8-bit address. Register `FF` however is reserved. Reading from register `FF` will begin a multiple read starting at address `0` but continuing until the complete buffer has been transferred, which may be more than 256 bytes. + +The RGB write packet is implemented as in stock factory firmware. The Basic write packet is not implemented. + +The LED microcontroller polls the Host for a packet once approximately every millisecond. This should not be relied upon as a guarantee. + +The following additional packets are implemented: + +### RGB write (custom) [`5B`] + +```c +struct { + uint8_t u8Cmd = 0x5B; + uint8_t u8GroundBrightness; + uint8_t u8TowerBrightness; + grb_t aGround[31]; + grb_t aTowerLeft[24]; + grb_t aTowerRight[24]; +} +``` + +### HSV write [`5C`] + +```c +struct { + uint8_t u8Cmd = 0x5C; + uint8_t u8GroundBrightness; + uint8_t u8TowerBrightness; + hsv_t aGround[31]; + hsv_t aTowerLeft[24]; + hsv_t aTowerRight[24]; +}; +``` + +### Mixed RGB and HSV write [`5D`] + +This packet transmits RGB data for the ground slider, but HSV for the towers. The rationale here is that the game sends data in RGB for the slider, which we use, but for the towers we wish to continue using our custom lighting. + +```c +struct { + uint8_t u8Cmd = 0x5C; + uint8_t u8GroundBrightness; + uint8_t u8TowerBrightness; + grb_t aGround[31]; + hsv_t aTowerLeft[24]; + hsv_t aTowerRight[24]; +}; +``` + +### Flash read [`5E`] + +Read 32 bytes from flash at the provided offset. The LED microcontroller will respond to this packet by writing four bytes, starting at address 0 (in a single transfer). + +```c +struct { + uint8_t u8Cmd = 0x5E; + uint32_t u32Offset; +}; +``` + +### Enter LDROM [`5F`] + +Instruct the LED microcontroller to enter LDROM. After transmission of this packet the Host microcontroller should transition to driving both I2C lines low if it wishes the LED bootloader to enter programming mode. + +Alternatively, the Host may drive both I2C low at any time outside of an I2C transmission, and the LED microcontroller will enter LDROM. This check is performed before the LED microcontroller begins its I2C read, and as such occurs at the same frequency. + +```c +struct { + uint8_t u8Cmd = 0x5F; +}; +``` diff --git a/docs/Protocol_PSoC.md b/docs/Protocol_PSoC.md new file mode 100644 index 0000000..c18c5e6 --- /dev/null +++ b/docs/Protocol_PSoC.md @@ -0,0 +1,22 @@ +# PSoC communication protocol +Commands to and from the PSoC follow the structure + +```c +{ + uint8_t bCommand; + uint8_t bLength; + uint8_t bData0; + uint8_t bData1; + uint8_t bChecksum; +} +``` + +`bChecksum` is the sum of the preceding four bytes. + +For requests, `bLength` is always `2`, and this is validated aggressively on the PSoC. + +**There is no synchronisation mechanism** and the existing PSoC implementation is brittle. If sync is lost between the PSoC and Host this is unrecoverable without a reset. + +If the appropriate debug flags have been set on the PSoC, it will transmit `00` before calling `CapSense_Start(); CapSense_InitializeAllBaselines()`, and `FF` afterwards. These bytes do not follow the standard packet structure. + +The PSoC transmits `C1` once when it has completed its first initialisation. This does not follow the standard packet structure. There is no mechanism to hard reset the PSoC, so if the Host resets for any reason other than a hardware power cycle, this byte will not be observed. diff --git a/docs/Protocols.md b/docs/Protocols.md new file mode 100644 index 0000000..c8defdc --- /dev/null +++ b/docs/Protocols.md @@ -0,0 +1,2 @@ +# TASOLLER Protocols +The TASOLLER is comprised of four ICs which all communicate between each other. diff --git a/generic.mk b/generic.mk index 6bb33b5..8b5bcc6 100644 --- a/generic.mk +++ b/generic.mk @@ -37,4 +37,4 @@ clean: $(shell mkdir $(OBJ_DIR)) -.PHONY: all clean +.PHONY: all clean $(SRC_DIR)/timestamp.c diff --git a/src/descriptors.c b/src/descriptors.c index 4d5cced..4ed45a6 100644 --- a/src/descriptors.c +++ b/src/descriptors.c @@ -1,41 +1,42 @@ #include +#include "sys/syslimits.h" #include "tasoller.h" +#define __ static const uint8_t IO4_ReportDescriptor[] = { // Analog input (28 bytes) HID_USAGE_PAGE(GENERIC_DESKTOP), HID_USAGE(JOYSTICK), HID_COLLECTION(APPLICATION), - HID_REPORT_ID(HID_REPORT_ID_IO4), - HID_USAGE(POINTER), - HID_COLLECTION(PHYSICAL), + __ HID_REPORT_ID(HID_REPORT_ID_IO4), + __ HID_USAGE(POINTER), + __ HID_COLLECTION(PHYSICAL), // 8 ADC channels - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), + __ __ HID_USAGE(X), + __ __ HID_USAGE(Y), + __ __ HID_USAGE(X), + __ __ HID_USAGE(Y), + __ __ HID_USAGE(X), + __ __ HID_USAGE(Y), + __ __ HID_USAGE(X), + __ __ HID_USAGE(Y), // 4 Rotary channels - HID_USAGE(RX), - HID_USAGE(RY), - HID_USAGE(RX), - HID_USAGE(RY), + __ __ HID_USAGE(RX), + __ __ HID_USAGE(RY), + __ __ HID_USAGE(RX), + __ __ HID_USAGE(RY), // 2 Coin chutes - HID_USAGE(SLIDER), - HID_USAGE(SLIDER), - HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(4, 65534), - HID_PHYSICAL_MINIMUM(1, 0), - HID_PHYSICAL_MAXIMUM(4, 65534), - HID_REPORT_COUNT(14), - HID_REPORT_SIZE(16), - HID_INPUT(DATA, VARIABLE, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION), - HID_END_COLLECTION(PHYSICAL), - + __ __ HID_USAGE(SLIDER), + __ __ HID_USAGE(SLIDER), + __ __ HID_LOGICAL_MINIMUM(1, 0), + __ __ HID_LOGICAL_MAXIMUM(4, 65534), + __ __ HID_PHYSICAL_MINIMUM(1, 0), + __ __ HID_PHYSICAL_MAXIMUM(4, 65534), + __ __ HID_REPORT_COUNT(14), + __ __ HID_REPORT_SIZE(16), + __ __ HID_INPUT(DATA, VARIABLE, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION), + __ HID_END_COLLECTION(PHYSICAL), // Digital input (6 bytes = 48 bits) // [ 0~15]: Player 1 buttons // [16~31]: Player 2 buttons @@ -50,91 +51,142 @@ static const uint8_t IO4_ReportDescriptor[] = { // -> 01h: ? // -> 02h: ? // -> 04h: ? (is set on timeout) - HID_USAGE_PAGE(SIMULATION), - HID_USAGE_PAGE(BUTTONS), - HID_USAGE_MINIMUM(1, 1), - HID_USAGE_MAXIMUM(1, 48), - HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(1, 1), - HID_PHYSICAL_MAXIMUM(1, 1), - HID_REPORT_SIZE(1), - HID_REPORT_COUNT(48), - HID_INPUT(DATA, VARIABLE, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION), + __ HID_USAGE_PAGE(SIMULATION), + __ HID_USAGE_PAGE(BUTTONS), + __ HID_USAGE_MINIMUM(1, 1), + __ HID_USAGE_MAXIMUM(1, 48), + __ HID_LOGICAL_MINIMUM(1, 0), + __ HID_LOGICAL_MAXIMUM(1, 1), + __ HID_PHYSICAL_MAXIMUM(1, 1), + __ HID_REPORT_SIZE(1), + __ HID_REPORT_COUNT(48), + __ HID_INPUT(DATA, VARIABLE, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION), // Reserved for future use. Pad with null. (29 bytes) - HID_USAGE(UNDEFINED), - HID_REPORT_SIZE(8), - HID_REPORT_COUNT(29), - HID_INPUT(CONSTANT, ARRAY, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION), - HID_USAGE_PAGE2(2, 0xFFA0), // Vendor defined FF0A - HID_USAGE(UNDEFINED), + __ HID_USAGE(UNDEFINED), + __ HID_REPORT_SIZE(8), + __ HID_REPORT_COUNT(29), + __ HID_INPUT(CONSTANT, ARRAY, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION), + __ HID_USAGE_PAGE2(2, 0xFFA0), // Vendor defined FF0A + __ HID_USAGE(UNDEFINED), // General-purpose commands to the board. First byte is the command, then 62 data byte - HID_REPORT_ID(HID_REPORT_ID_IO4_CMD), - HID_COLLECTION(APPLICATION), - HID_USAGE(UNDEFINED), - HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(2, 255), - HID_REPORT_SIZE(8), - HID_REPORT_COUNT(63), - HID_OUTPUT(DATA, VARIABLE, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION, - NON_VOLATILE), - HID_END_COLLECTION(APPLICATION), + __ HID_REPORT_ID(HID_REPORT_ID_IO4_CMD), + __ HID_COLLECTION(APPLICATION), + __ __ HID_USAGE(UNDEFINED), + __ __ HID_LOGICAL_MINIMUM(1, 0), + __ __ HID_LOGICAL_MAXIMUM(2, 255), + __ __ HID_REPORT_SIZE(8), + __ __ HID_REPORT_COUNT(63), + __ __ HID_OUTPUT(DATA, VARIABLE, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION, + NON_VOLATILE), + __ HID_END_COLLECTION(APPLICATION), HID_END_COLLECTION(APPLICATION), }; +#define _FINGER_DESCRIPTOR \ + HID_USAGE(FINGER), /**/ \ + HID_COLLECTION(LOGICAL), /**/ \ + HID_USAGE(TIP_SWITCH), /**/ \ + HID_LOGICAL_MINIMUM(1, 0), /**/ \ + HID_LOGICAL_MAXIMUM(1, 1), /**/ \ + HID_REPORT_SIZE(1), /**/ \ + HID_REPORT_COUNT(1), /**/ \ + HID_INPUT(DATA, VARIABLE, ABSOLUTE), /**/ \ + HID_REPORT_COUNT(7), /**/ \ + HID_INPUT(CONSTANT, ARRAY, ABSOLUTE), /**/ \ + HID_REPORT_SIZE(8), /**/ \ + HID_USAGE(CONTACT_IDENTIFIER), /**/ \ + HID_REPORT_COUNT(1), /**/ \ + HID_INPUT(DATA, VARIABLE, ABSOLUTE), /**/ \ + HID_USAGE_PAGE(GENERIC_DESKTOP), /**/ \ + HID_LOGICAL_MAXIMUM(2, 127), /**/ \ + HID_REPORT_SIZE(8), /**/ \ + HID_REPORT_COUNT(2), /**/ \ + HID_PHYSICAL_MINIMUM(1, 0), /**/ \ + HID_PHYSICAL_MAXIMUM(2, 127), /**/ \ + HID_USAGE(X), /**/ \ + HID_USAGE(Y), /**/ \ + HID_INPUT(DATA, VARIABLE, ABSOLUTE), /**/ \ + HID_USAGE_PAGE(DIGITIZER), /**/ \ + HID_USAGE(WIDTH), /**/ \ + HID_USAGE(HEIGHT), /**/ \ + HID_INPUT(DATA, VARIABLE, ABSOLUTE), /**/ \ + HID_END_COLLECTION(LOGICAL) + static const uint8_t Keyboard_ReportDescriptor[] = { // Keyboard input report HID_USAGE_PAGE(GENERIC_DESKTOP), HID_USAGE(KEYBOARD), HID_COLLECTION(APPLICATION), - HID_REPORT_ID(HID_REPORT_ID_KEYBOARD), - - HID_USAGE_PAGE(KEYBOARD), - HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(2, 231), - HID_USAGE_MINIMUM(1, 0), - HID_USAGE_MAXIMUM(1, 231), - HID_REPORT_SIZE(8), - HID_REPORT_COUNT(NUM_FN + NUM_AIR + NUM_GROUND), - HID_INPUT(DATA, ARRAY, ABSOLUTE), - + __ HID_REPORT_ID(HID_REPORT_ID_KEYBOARD), + __ HID_USAGE_PAGE(KEYBOARD), + __ HID_LOGICAL_MINIMUM(1, 0), + __ HID_LOGICAL_MAXIMUM(2, 231), + __ HID_USAGE_MINIMUM(1, 0), + __ HID_USAGE_MAXIMUM(1, 231), + __ HID_REPORT_SIZE(8), + __ HID_REPORT_COUNT(NUM_FN + NUM_AIR + NUM_GROUND), + __ HID_INPUT(DATA, ARRAY, ABSOLUTE), HID_END_COLLECTION(APPLICATION), // Consumer control report HID_USAGE_PAGE(CONSUMER), HID_USAGE(CONSUMER_CONTROL), HID_COLLECTION(APPLICATION), - HID_REPORT_ID(HID_REPORT_ID_CONSUMER_CONTROL), - - HID_USAGE_PAGE(CONSUMER), - HID_USAGE_MINIMUM(1, 0), - HID_USAGE_MAXIMUM(2, 0x0FFF), - HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(2, 0x0FFF), - HID_REPORT_SIZE(16), - HID_REPORT_COUNT(2), - HID_INPUT(DATA, ARRAY, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION), - + __ HID_REPORT_ID(HID_REPORT_ID_CONSUMER_CONTROL), + __ HID_USAGE_PAGE(CONSUMER), + __ HID_USAGE_MINIMUM(1, 0), + __ HID_USAGE_MAXIMUM(2, 0x0FFF), + __ HID_LOGICAL_MINIMUM(1, 0), + __ HID_LOGICAL_MAXIMUM(2, 0x0FFF), + __ HID_REPORT_SIZE(16), + __ HID_REPORT_COUNT(2), + __ HID_INPUT(DATA, ARRAY, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION), HID_END_COLLECTION(APPLICATION), // Report for sending the enter key HID_USAGE_PAGE(GENERIC_DESKTOP), HID_USAGE(KEYBOARD), HID_COLLECTION(APPLICATION), - HID_REPORT_ID(HID_REPORT_ID_ENTER), + __ HID_REPORT_ID(HID_REPORT_ID_ENTER), + __ HID_USAGE_PAGE(KEYBOARD), + __ HID_LOGICAL_MINIMUM(1, 0), + __ HID_LOGICAL_MAXIMUM(2, 231), + __ HID_USAGE_MINIMUM(1, 0), + __ HID_USAGE_MAXIMUM(1, 231), + __ HID_REPORT_SIZE(8), + __ HID_REPORT_COUNT(1), + __ HID_INPUT(DATA, ARRAY, ABSOLUTE), + HID_END_COLLECTION(APPLICATION), - HID_USAGE_PAGE(KEYBOARD), - HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(2, 231), - HID_USAGE_MINIMUM(1, 0), - HID_USAGE_MAXIMUM(1, 231), - HID_REPORT_SIZE(8), +#ifdef ENABLE_TOUCH_INPUT + // Touch input report + HID_USAGE_PAGE(DIGITIZER), + HID_USAGE(TOUCH_SCREEN), + HID_COLLECTION(APPLICATION), + HID_REPORT_ID(HID_REPORT_ID_TOUCH), + + _FINGER_DESCRIPTOR, + _FINGER_DESCRIPTOR, + _FINGER_DESCRIPTOR, + _FINGER_DESCRIPTOR, + _FINGER_DESCRIPTOR, + _FINGER_DESCRIPTOR, + _FINGER_DESCRIPTOR, + _FINGER_DESCRIPTOR, + + HID_USAGE_PAGE(DIGITIZER), + HID_USAGE(CONTACT_COUNT), + HID_LOGICAL_MAXIMUM(1, 127), HID_REPORT_COUNT(1), - HID_INPUT(DATA, ARRAY, ABSOLUTE), + HID_REPORT_SIZE(8), + HID_INPUT(DATA, VARIABLE, ABSOLUTE), + // Aren't we meant to have contact count maximum? (id 55h) HID_END_COLLECTION(APPLICATION), +#endif }; usb_device_descr_t gIO4DeviceDescriptor = { @@ -179,8 +231,8 @@ typedef struct __packed { * We're meant to have an OUT endpoint for IO4, but IO4 uses the Win32 WriteFile API, which will * happily fall back to SET_REPORT calls on the control endpoints if there's no OUT endpoint. * - * Chunithm never uses the output capability of IO4, so we have no high-frequency data that - * might choke our control endpoints--we just need to support the initial configuration packets. + * IO4 receives OUT packets at a 47ms interval on average, 25%=46.8ms, 75%=61.3ms + * As such, we're not going to saturate our control endpoint by bootlegging off it for HID! */ // const usb_desc_endpoint_t HID_EndpointOut; @@ -316,7 +368,7 @@ static const config_desc_t gConfigDescriptor = { DESC_ENDPOINT, USBD_HID_IO4_EP_IN, EP_INT, - USBD_HID_BUF_LEN, + USBD_HID_BUF_LEN_IO4, HID_IO4_INT_IN_INTERVAL, }, @@ -346,7 +398,7 @@ static const config_desc_t gConfigDescriptor = { DESC_ENDPOINT, USBD_HID_MISC_EP_IN, EP_INT, - USBD_HID_BUF_LEN, + USBD_HID_BUF_LEN_IN, HID_DEFAULT_INT_IN_INTERVAL, }, { @@ -354,7 +406,7 @@ static const config_desc_t gConfigDescriptor = { DESC_ENDPOINT, USBD_HID_MISC_EP_OUT, EP_INT, - USBD_HID_BUF_LEN, + USBD_HID_BUF_LEN_OUT, HID_DEFAULT_INT_IN_INTERVAL, }, }; diff --git a/src/fmc.c b/src/fmc.c index 1b5b317..887a027 100644 --- a/src/fmc.c +++ b/src/fmc.c @@ -56,14 +56,21 @@ void FMC_EEPROM_Load(void) { gConfig.u8Sens = 8; - gConfig.u16HueWingLeft = 330; - gConfig.u16HueWingRight = 180; - // TODO: These are the DJ DAO defaults, but the game looks like the hue should be 60 and 300 - gConfig.u16HueGround = 45; + gConfig.u16HueTowerLeft = 330; + gConfig.u16HueTowerRight = 180; + +#ifdef LED_CORRECTION_ON_LED_MCU + gConfig.u16HueGround = 60; + gConfig.u16HueGroundActive = 300; +#else + // The game uses 60° and 300°. Our red channel is approximately half as strong as the game, + // so by shifting 30° we get the appearance of correct colours. + gConfig.u16HueGround = 30; gConfig.u16HueGroundActive = 330; +#endif gConfig.u8LedGroundBrightness = 255; - gConfig.u8LedWingBrightness = 255; + gConfig.u8LedTowerBrightness = 255; for (uint8_t i = 0; i < 32; i++) { gConfig.u16PSoCScaleMin[i] = 0; @@ -72,6 +79,11 @@ void FMC_EEPROM_Load(void) { bConfigDirty = 1; } + // If it's an invalid value, it's probably from uninitialized flash; don't boot to the + // bootloader! + if (!(gConfig.u8NextBootLEDBootloader == 0 || gConfig.u8NextBootLEDBootloader == 1)) { + gConfig.u8NextBootLEDBootloader = 0; + } FMC_Close(); } diff --git a/src/fmc_user.h b/src/fmc_user.h index 04cabc7..841cd40 100644 --- a/src/fmc_user.h +++ b/src/fmc_user.h @@ -44,17 +44,20 @@ typedef struct __attribute__((aligned(4), packed)) { // Only needs 4 bits but we aren't short atm uint8_t u8Sens; // [1~16], Higher = more sensitive - uint16_t u16HueWingLeft; - uint16_t u16HueWingRight; + uint16_t u16HueTowerLeft; + uint16_t u16HueTowerRight; uint16_t u16HueGround; uint16_t u16HueGroundActive; uint8_t u8LedGroundBrightness; - uint8_t u8LedWingBrightness; + uint8_t u8LedTowerBrightness; // Calibration data uint16_t u16PSoCScaleMin[32]; uint16_t u16PSoCScaleMax[32]; + + // State data + uint8_t u8NextBootLEDBootloader; } flash_t; extern flash_t gConfig; extern uint8_t bConfigDirty; diff --git a/src/hid.c b/src/hid.c index 37a9ebf..e400e94 100644 --- a/src/hid.c +++ b/src/hid.c @@ -129,10 +129,140 @@ static uint8_t _HID_Enter_Tick(void) { return 1; } +#ifdef ENABLE_TOUCH_INPUT +enum { + u8MaxFingers = (sizeof((hid_touch_report_t *)(0))->sFinger) / + (sizeof((hid_touch_report_t *)(0))->sFinger[0]) +}; +#define OVERLAP(start1, end1, start2, end2) (Minimum((end2), (end1)) >= Maximum((start1), (start2))) +typedef struct { + uint8_t bActive; + uint8_t u8Start; + uint8_t u8End; + + uint8_t u8FakeCentreX; + uint8_t u8FakeCentreY; +} tracked_finger_t; +tracked_finger_t trackedFingers[u8MaxFingers] = { 0 }; + +#define SCREEN_HEIGHT 127 +#define SCREEN_WIDTH 128 +// Definition for Sonolus on my Pixel 6 +// #define LEFT_EDGE 28 +// #define RIGHT_EDGE 20 +// Definition for PJSK on my Pixel 6 +// #define LEFT_EDGE 22 +// #define RIGHT_EDGE 20 +#define LEFT_EDGE 0 +#define RIGHT_EDGE 0 + +#define VERTICAL_MOVEMENT 10 + +#define SCREEN_Y (SCREEN_HEIGHT / 5) +#define TOUCH_WIDTH ((SCREEN_WIDTH - LEFT_EDGE - RIGHT_EDGE) / 16) + +static void _TrackTouches(void) { + static tracked_finger_t newFingers[u8MaxFingers]; + memset(newFingers, 0, sizeof newFingers); + + uint8_t nFingers = 0; + int8_t iStart = -1; + + static struct { + uint16_t u16Bias; + uint8_t u8Pos; + } sBiasCalc[16] = { 0 }; + uint16_t u16BiasTop = 0; + uint16_t u16BiasBottom = 0; + + // Identify our currently active fingers + for (uint8_t x = 0; x <= 16; x++) { + if ((x < 16) && gu16PSoCDigital & (1 << x)) { + if (iStart == -1) { + u16BiasTop = 0; + u16BiasBottom = 0; + iStart = x; + } + + // Position = [LEFT_EDGE, {scale based on X} , RIGHT_EDGE] = SCREEN_WIDTH + // Virtual width = SCREEN_WIDTH - LEFT_EDGE - RIGHT_EDGE + + // L + ((W-L-R) * (16 - x)) / 16) + + // TODO: Might need -1 here not +1 + sBiasCalc[x - iStart].u8Pos = LEFT_EDGE + ((SCREEN_WIDTH - LEFT_EDGE - RIGHT_EDGE) * (32 - (x * 2 + 1))) / 32; + // sBiasCalc[x - iStart].u8Pos -= + // (LEFT_EDGE + ((16 - x) * TOUCH_WIDTH) - (TOUCH_WIDTH / 2)); + sBiasCalc[x - iStart].u16Bias = gu8GroundData[x * 2] + gu8GroundData[x * 2 + 1]; + + u16BiasTop += gu8GroundData[x * 2]; + u16BiasBottom += gu8GroundData[x * 2 + 1]; + } else { + if (iStart != -1) { + newFingers[nFingers].bActive = 1; + newFingers[nFingers].u8Start = iStart; + newFingers[nFingers].u8End = x - 1; + + uint32_t u32TotalWeight = 0; + uint64_t u64Biased = 0; + for (uint8_t i = 0; i < x - iStart; i++) { + u32TotalWeight += (uint32_t)sBiasCalc[i].u16Bias; + u64Biased += (uint64_t)sBiasCalc[i].u8Pos * (uint64_t)sBiasCalc[i].u16Bias; + } + newFingers[nFingers].u8FakeCentreX = u64Biased / u32TotalWeight; + newFingers[nFingers].u8FakeCentreY = (u16BiasTop * VERTICAL_MOVEMENT) / (u16BiasTop + u16BiasBottom); + + nFingers++; + iStart = -1; + } + } + if (nFingers >= u8MaxFingers) break; + } + + memcpy(trackedFingers, newFingers, sizeof newFingers); +} +static uint8_t _HID_Touch_Tick(void) { + hid_touch_report_t *buf = (hid_touch_report_t *)HID_MISC_BUF; + + memset(buf, 0, sizeof *buf); + buf->bReportId = HID_REPORT_ID_TOUCH; + + _TrackTouches(); + + // When in portrait, X and Y make sense + // In landscape, X and Y are inverse (ie screen rotation is ignored) + + uint8_t nFingers = 0; + for (uint8_t i = 0; i < u8MaxFingers; i++) { + buf->sFinger[nFingers].bTipSwitch = trackedFingers[i].bActive; + buf->sFinger[nFingers].bIdentifier = i; + + if (trackedFingers[i].bActive) { + uint8_t width = (trackedFingers[i].u8End + 1) - trackedFingers[i].u8Start; + buf->sFinger[nFingers].bX = SCREEN_Y + trackedFingers[i].u8FakeCentreY; + buf->sFinger[nFingers].bY = trackedFingers[i].u8FakeCentreX; + + buf->sFinger[nFingers].bW = TOUCH_WIDTH / 2; + buf->sFinger[nFingers].bH = width * TOUCH_WIDTH; + } + + nFingers++; + } + + buf->bContactCount = nFingers; + + HID_MISC_SEND(buf); + return 0; +} +#endif + typedef enum { TIMESLOT_KEYBOARD = 0, TIMESLOT_CONSUMER, TIMESLOT_ENTER, +#ifdef ENABLE_TOUCH_INPUT + TIMESLOT_TOUCH, +#endif _TIMESLOT_COUNT, } eTimeslot_t; static void _HID_Misc_Tick(void) { @@ -166,6 +296,12 @@ static void _HID_Misc_Tick(void) { if (_HID_Enter_Tick()) goto timeslot_used; break; +#ifdef ENABLE_TOUCH_INPUT + case TIMESLOT_TOUCH: + if (_HID_Touch_Tick()) goto timeslot_used; + break; +#endif + default: break; } diff --git a/src/hid_def.h b/src/hid_def.h index 0aefc4c..125e7d2 100644 --- a/src/hid_def.h +++ b/src/hid_def.h @@ -16,6 +16,7 @@ enum { HID_REPORT_ID_KEYBOARD, HID_REPORT_ID_CONSUMER_CONTROL, HID_REPORT_ID_ENTER, + HID_REPORT_ID_TOUCH, HID_REPORT_ID_IO4_CMD = 16, }; @@ -37,6 +38,40 @@ typedef struct __packed { uint8_t bReportId; uint8_t u8Keyboard[1]; } hid_enter_report_t; +typedef struct __packed { + uint8_t bReportId; + + /* + uint8_t bTipSwitch : 1; + uint8_t _1_3 : 3; + uint8_t bInRange : 1; + // uint8_t bConfidence : 1; + uint16_t _5_11 : 11; + uint16_t wX; + uint16_t wY; + uint16_t wWidth; + uint16_t wHeight; + uint16_t _80_16 : 16; + */ + + // uint8_t bTipSwitch : 1; + // uint8_t bInRange : 1; + // uint8_t _2_6 : 6; + // uint8_t _8; + // uint16_t wX; + // uint16_t wY; + + struct { + uint8_t bTipSwitch : 1; + uint8_t _1_7 : 1; + uint8_t bIdentifier; + uint8_t bX; + uint8_t bY; + uint8_t bW; + uint8_t bH; + } sFinger[8]; + uint8_t bContactCount; +} hid_touch_report_t; typedef struct __packed { uint8_t bReportId; diff --git a/src/io4.c b/src/io4.c index 53a68b0..0e9cb29 100644 --- a/src/io4.c +++ b/src/io4.c @@ -58,16 +58,6 @@ void IO4_HID_Tick(void) { } void IO4_Control(uint8_t u8Cmd, uint32_t u32Size, volatile uint8_t *pu8Buffer) { - /** - * Setup sequence: - * - * - IO4_CMD_CLEAR_BOARD_STATUS - * - IO4_CMD_SET_COMM_TIMEOUT (00) - * - IO4_CMD_SET_SAMPLING_COUNT (06) - * - IO4_CMD_SET_GENERAL_OUTPUT (00 00 00) - * - IO4_CMD_SET_UNIQUE_OUTPUT (38 00 00 00 ..) - */ - switch (u8Cmd) { case IO4_CMD_SET_COMM_TIMEOUT: if (u32Size >= 1) { diff --git a/src/led.c b/src/led.c index cab5f8a..9da3a7d 100644 --- a/src/led.c +++ b/src/led.c @@ -168,17 +168,17 @@ static const uint8_t _LED_GroundBrightness(void) { } return gConfig.u8LedGroundBrightness; } -static const uint8_t _LED_WingBrightness(void) { - if (gbLedDataIsControlledInt) return gConfig.u8LedWingBrightness; +static const uint8_t _LED_TowerBrightness(void) { + if (gbLedDataIsControlledInt) return gConfig.u8LedTowerBrightness; if (g_u8UsbState == USB_STATE_SUSPEND && gu32NowMs > 5000) return 0; if (gbLedDataIsControlledExt) - return ((uint16_t)gConfig.u8LedGroundBrightness * (uint16_t)gu8IO4PWMScale) / 255; - return gConfig.u8LedWingBrightness; + return ((uint16_t)gConfig.u8LedTowerBrightness * (uint16_t)gu8IO4PWMScale) / 255; + return gConfig.u8LedTowerBrightness; } static inline void _LED_SetPower(void) { PIN_LED_GROUND_PWR = _LED_GroundBrightness() ? 1 : 0; - PIN_LED_WING_PWR = _LED_WingBrightness() ? 1 : 0; + PIN_LED_TOWER_PWR = _LED_TowerBrightness() ? 1 : 0; } void LED_Write(void) { @@ -195,25 +195,25 @@ void LED_Write(void) { if (gbLedDataIsControlledInt) { pTxHSV->u8Cmd = LED_CMD_CUSTOM_HSV; pTxHSV->u8GroundBrightness = _LED_GroundBrightness(); - pTxHSV->u8WingBrightness = _LED_WingBrightness(); + pTxHSV->u8TowerBrightness = _LED_TowerBrightness(); _LED_SetPower(); LED_Ground_Internal_HSV(pTxHSV->aGround); - LED_Wings_Reactive_HSV(&pTxHSV->Wings); + LED_Towers_Reactive_HSV(&pTxHSV->Towers); } else if (gbLedDataIsControlledExt) { // RGB control from the game pTxRGB->u8Cmd = LED_CMD_CUSTOM_RGB; pTxRGB->u8GroundBrightness = _LED_GroundBrightness(); - pTxRGB->u8WingBrightness = _LED_WingBrightness(); + pTxRGB->u8TowerBrightness = _LED_TowerBrightness(); _LED_SetPower(); LED_Ground_Controlled_RGB(pTxRGB->aGround); - LED_Wings_Controlled_RGB(&pTxRGB->Wings); + LED_Towers_Controlled_RGB(&pTxRGB->Towers); } else { // User-set coloring scheme pTxHSV->u8Cmd = LED_CMD_CUSTOM_HSV; pTxHSV->u8GroundBrightness = _LED_GroundBrightness(); - pTxHSV->u8WingBrightness = _LED_WingBrightness(); + pTxHSV->u8TowerBrightness = _LED_TowerBrightness(); _LED_SetPower(); if (gConfig.bEnableRainbow) { @@ -221,7 +221,7 @@ void LED_Write(void) { } else { LED_Ground_Static_HSV(pTxHSV->aGround); } - LED_Wings_Reactive_HSV(&pTxHSV->Wings); + LED_Towers_Reactive_HSV(&pTxHSV->Towers); } } diff --git a/src/led.h b/src/led.h index 7c460d4..4984697 100644 --- a/src/led.h +++ b/src/led.h @@ -19,22 +19,22 @@ extern volatile uint16_t u16I2CRxIndex; extern uint8_t gbLedIsCustom; // === Assigners (led_impl.c) === -void LED_Wings_Reactive_HSV(_led_wings_hsv* pWings); +void LED_Towers_Reactive_HSV(_led_towers_hsv* pTowers); void LED_Ground_Rainbow_HSV(hsv_t* aGround); void LED_Ground_Static_HSV(hsv_t* aGround); void LED_Ground_Internal_HSV(hsv_t* aGround); /** All "controlled" data is in RGB format, so these aren't a thing - * void LED_Wings_Controlled_HSV(_led_wings_hsv* pWings); + * void LED_Towers_Controlled_HSV(_led_towers_hsv* pTowers); * void LED_Ground_Controlled_HSV(hsv_t* aGround); */ // Unimplemented nonsense stuff -void LED_Wings_Reactive_RGB(_led_wings_rgb* pWings); -void LED_Ground_Rainbow_RGB(rgb_t* pWings); +void LED_Towers_Reactive_RGB(_led_towers_rgb* pTowers); +void LED_Ground_Rainbow_RGB(rgb_t* pTowers); void LED_Ground_Internal_RGB(rgb_t* aGround); void LED_Ground_Static_RGB(rgb_t* aGround); // Actual RGBs that're used -void LED_Wings_Controlled_RGB(_led_wings_rgb* pWings); +void LED_Towers_Controlled_RGB(_led_towers_rgb* pTowers); void LED_Ground_Controlled_RGB(rgb_t* aGround); // === Exported functions === diff --git a/src/led_impl.c b/src/led_impl.c index edb0324..21d405f 100644 --- a/src/led_impl.c +++ b/src/led_impl.c @@ -1,3 +1,5 @@ +#include + #include "tasoller.h" static inline uint8_t LED_ScaleU8(uint8_t u8V, uint8_t u8Scale) { @@ -10,7 +12,33 @@ static inline uint8_t LED_ScaleU8(uint8_t u8V, uint8_t u8Scale) { #endif } -static const uint8_t su8aWingSensors[LED_NUM_WING] = { +#define INDEX_IS_CELL(x) ((x) % 2 == 0) +#define INDEX_IS_ACTIVE_CELL(x) (gu16PSoCDigital & (1 << ((x) >> 1))) +#define INDEX_IS_SEPARATING_DIVIDER(x) (((x) >> 1) % 4 == 3) +#define INDEX_IS_LIT_DIVIDER(x) \ + (gu16PSoCDigital & (1 << ((x) >> 1)) && gu16PSoCDigital & (1 << (((x) >> 1) + 1))) + +typedef enum { + // A cell that's currently active + IndexType_CellActive, + // A cell that isn't currently active + IndexType_Cell, + // A divider that's being lit up because it's part of the visual grouping + IndexType_SeparatingDivider, + // A divider that's being lit up because both cells either side are active + IndexType_LitDivider, + // A divider that's not being lit up + IndexType_Divider, +} IndexType_t; + +#define INDEX_TYPE(x) \ + (IndexType_t)((INDEX_IS_CELL(x) \ + ? (INDEX_IS_ACTIVE_CELL(x) ? IndexType_CellActive : IndexType_Cell) \ + : INDEX_IS_SEPARATING_DIVIDER(x) ? IndexType_SeparatingDivider \ + : INDEX_IS_LIT_DIVIDER(x) ? IndexType_LitDivider \ + : IndexType_Divider)) + +static const uint8_t su8aTowerSensors[LED_NUM_TOWER] = { DIGITAL_AIR1_Msk, DIGITAL_AIR1_Msk, DIGITAL_AIR1_Msk, DIGITAL_AIR1_Msk, // DIGITAL_AIR2_Msk, DIGITAL_AIR2_Msk, DIGITAL_AIR2_Msk, DIGITAL_AIR2_Msk, // DIGITAL_AIR3_Msk, DIGITAL_AIR3_Msk, DIGITAL_AIR3_Msk, DIGITAL_AIR3_Msk, // @@ -21,92 +49,194 @@ static const uint8_t su8aWingSensors[LED_NUM_WING] = { #define SATURATION_ACTIVE 255 #define SATURATION_INACTIVE 240 -#define VALUE_ACTIVE (gConfig.u8LedWingBrightness) -#define VALUE_INACTIVE (gConfig.u8LedWingBrightness / 2) +#define VALUE_ACTIVE (gConfig.u8LedTowerBrightness) +#define VALUE_INACTIVE (gConfig.u8LedTowerBrightness / 2) -void LED_Wings_Reactive_HSV(_led_wings_hsv* pWings) { - for (uint8_t i = 0; i < LED_NUM_WING; i++) { - // Left wing - if (gu8DigitalButtons & su8aWingSensors[LED_NUM_WING - i - 1]) { - pWings->aWingL[i].h = gConfig.u16HueWingLeft; - pWings->aWingL[i].s = SATURATION_ACTIVE; - pWings->aWingL[i].v = VALUE_ACTIVE; +void LED_Towers_Reactive_HSV(_led_towers_hsv* pTowers) { + for (uint8_t i = 0; i < LED_NUM_TOWER; i++) { + // Left tower + if (gu8DigitalButtons & su8aTowerSensors[LED_NUM_TOWER - i - 1]) { + pTowers->aTowerL[i].h = gConfig.u16HueTowerLeft; + pTowers->aTowerL[i].s = SATURATION_ACTIVE; + pTowers->aTowerL[i].v = VALUE_ACTIVE; } else { - pWings->aWingL[i].h = gConfig.u16HueWingLeft; - pWings->aWingL[i].s = SATURATION_INACTIVE; - pWings->aWingL[i].v = VALUE_INACTIVE; + pTowers->aTowerL[i].h = gConfig.u16HueTowerLeft; + pTowers->aTowerL[i].s = SATURATION_INACTIVE; + pTowers->aTowerL[i].v = VALUE_INACTIVE; } - // Right wing - if (gu8DigitalButtons & su8aWingSensors[i]) { - pWings->aWingR[i].h = gConfig.u16HueWingRight; - pWings->aWingR[i].s = SATURATION_ACTIVE; - pWings->aWingR[i].v = VALUE_ACTIVE; + // Right tower + if (gu8DigitalButtons & su8aTowerSensors[i]) { + pTowers->aTowerR[i].h = gConfig.u16HueTowerRight; + pTowers->aTowerR[i].s = SATURATION_ACTIVE; + pTowers->aTowerR[i].v = VALUE_ACTIVE; } else { - pWings->aWingR[i].h = gConfig.u16HueWingRight; - pWings->aWingR[i].s = SATURATION_INACTIVE; - pWings->aWingR[i].v = VALUE_INACTIVE; + pTowers->aTowerR[i].h = gConfig.u16HueTowerRight; + pTowers->aTowerR[i].s = SATURATION_INACTIVE; + pTowers->aTowerR[i].v = VALUE_INACTIVE; } } } + void LED_Ground_Rainbow_HSV(hsv_t* aGround) { - // 5 ticks * 360 hue = 1800 calls for one cycle (1.8s) - static uint16_t u16Hue = 0; + static struct { + uint8_t value; + uint8_t direction; + } twinkle[LED_NUM_GROUND_LOGICAL] = { 0 }; + + // A true blue or true green will have a different brightness to other colours (currently) so + // avoid that awkward discoloration. + static const uint16_t u16HueOffset = 2; + static uint8_t u8Ticker = 0; if (++u8Ticker == 5) { u8Ticker = 0; - u16Hue++; - if (u16Hue == LED_HUE_MAX) u16Hue = 0; + // 5 ticks * 360 hue = 1800 calls for one cycle (1.8s) + // u16HueOffset++ or smth, I forgot + + // Twinkle animation + // for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { + // if (twinkle[i].direction) { + // if (rand() < RAND_MAX / 256) { + // twinkle[i].direction = 0; + // } + // } else { + // if (rand() < RAND_MAX / 512) { + // twinkle[i].direction = 1; + // } + // } + // + // if (twinkle[i].direction) { + // if (twinkle[i].value < 255) twinkle[i].value++; + // } else { + // if (twinkle[i].value > 0) twinkle[i].value--; + // } + // } + + // Fade-out animation + for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { + switch (INDEX_TYPE(i)) { + case IndexType_CellActive: + // u8New = (gu8GroundData[(i >> 1) * 2] / 2 + gu8GroundData[(i >> 1) * 2 + 1] / + // 2); if (u8New > twinkle[i].value) twinkle[i].value = u8New; break; + case IndexType_LitDivider: + // u8New = + // (gu8GroundData[(i >> 1) * 2] / 4 + gu8GroundData[(i >> 1) * 2 + 1] / 4 + + // gu8GroundData[(i >> 1) * 2 + 2] / 4 + gu8GroundData[(i >> 1) * 2 + 3] / + // 4); + // u8New = 255; + // if (u8New > twinkle[i].value) twinkle[i].value = u8New; + + twinkle[i].value = 255; + break; + default: + if (twinkle[i].value > 200) + twinkle[i].value -= 8; + else if (twinkle[i].value > 150) + twinkle[i].value -= 6; + else if (twinkle[i].value > 100) + twinkle[i].value -= 4; + else if (twinkle[i].value > 50) + twinkle[i].value -= 1; + if (twinkle[i].value < 50) twinkle[i].value = 50; + break; + } + } } for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { - uint16_t h = 0; - uint8_t v = 190; - uint8_t nCell = i >> 1; - if (i % 2 == 0) { - if (gu16PSoCDigital & (1 << nCell)) { - v = 255; - h = LED_HUE_MAX / 2; + const uint16_t u16H = + (u16HueOffset + (i * (LED_HUE_MAX / (LED_NUM_GROUND_LOGICAL - 1)))) % LED_HUE_MAX; + + if (1) { + switch (INDEX_TYPE(i)) { + case IndexType_CellActive: + case IndexType_LitDivider: + case IndexType_SeparatingDivider: + aGround[i].h = u16H; + aGround[i].s = 255; + aGround[i].v = 255; + break; + case IndexType_Cell: + case IndexType_Divider: + aGround[i].h = u16H; + aGround[i].s = 255; + + // aGround[i].v = 20 + (twinkle[i].value / 4); + aGround[i].v = twinkle[i].value; + break; } - } else if (nCell % 4 == 3) { - h = LED_HUE_MAX / 2; + + continue; } - aGround[i].h = (u16Hue + h + (i * (LED_HUE_MAX / LED_NUM_GROUND_LOGICAL))) % LED_HUE_MAX; - aGround[i].s = v; - aGround[i].v = v - 63; + const uint16_t u16Value = 50; + const uint16_t u16ValueActive = 255; + + uint8_t u8Value; + switch (INDEX_TYPE(i)) { + case IndexType_CellActive: + // aGround[i].h = u16H; + // aGround[i].s = 255; + // aGround[i].v = u16ValueActive; + // break; + case IndexType_Cell: + aGround[i].h = u16H; + aGround[i].s = 255; + + u8Value = gu8GroundData[(i >> 1) * 2] / 2 + gu8GroundData[(i >> 1) * 2 + 1] / 2; + aGround[i].v = u8Value; + break; + case IndexType_SeparatingDivider: + aGround[i].h = u16H; + aGround[i].s = 255; + aGround[i].v = u16ValueActive; + break; + case IndexType_LitDivider: + // aGround[i].h = u16H; + // aGround[i].s = 255; + // aGround[i].v = u16ValueActive; + // break; + case IndexType_Divider: + aGround[i].h = u16H; + aGround[i].s = 255; + + u8Value = gu8GroundData[(i >> 1) * 2] / 4 + gu8GroundData[(i >> 1) * 2 + 1] / 4 + + gu8GroundData[(i >> 1) * 2 + 2] / 4 + gu8GroundData[(i >> 1) * 2 + 3] / 4; + aGround[i].v = u8Value; + break; + } } } void LED_Ground_Static_HSV(hsv_t* aGround) { for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { - const uint8_t nCell = i >> 1; - if (i % 2 == 0) { - // This is a cell. Light it according to the touch input - if (gu16PSoCDigital & (1 << nCell)) { + switch (INDEX_TYPE(i)) { + case IndexType_CellActive: aGround[i].h = gConfig.u16HueGroundActive; aGround[i].s = 255; aGround[i].v = 255; - } else { + break; + case IndexType_Cell: aGround[i].h = gConfig.u16HueGround; aGround[i].s = 255; aGround[i].v = 255; - } - } else if (nCell % 4 == 3) { - // This is a separating divider. Light it with the active colour - aGround[i].h = gConfig.u16HueGroundActive; - aGround[i].s = 255; - aGround[i].v = 255; - } else { - // This is a non-separating divider. Light it based on the two cells either side - if (gu16PSoCDigital & (1 << nCell) && gu16PSoCDigital & (1 << (nCell + 1))) { + + break; + case IndexType_SeparatingDivider: aGround[i].h = gConfig.u16HueGroundActive; aGround[i].s = 255; aGround[i].v = 255; - } else { + break; + case IndexType_LitDivider: + aGround[i].h = gConfig.u16HueGroundActive; + aGround[i].s = 255; + aGround[i].v = 255; + break; + case IndexType_Divider: aGround[i].h = gConfig.u16HueGround; aGround[i].s = 255; aGround[i].v = 255; - } + break; } } } @@ -169,41 +299,41 @@ void HsvToHost(rgb_t* pRGB, uint16_t u16H, uint8_t u8S, uint8_t u8V) { return; } -void LED_Wings_Reactive_RGB(_led_wings_rgb* pWings) { +void LED_Towers_Reactive_RGB(_led_towers_rgb* pTowers) { rgb_t u8aRgbActive; rgb_t u8aRgbInactive; uint8_t i; - // Left wing - HsvToHost(&u8aRgbActive, gConfig.u16HueWingLeft, SATURATION_ACTIVE, VALUE_ACTIVE); - HsvToHost(&u8aRgbInactive, gConfig.u16HueWingLeft, SATURATION_INACTIVE, VALUE_INACTIVE); - for (i = 0; i < LED_NUM_WING; i++) { + // Left tower + HsvToHost(&u8aRgbActive, gConfig.u16HueTowerLeft, SATURATION_ACTIVE, VALUE_ACTIVE); + HsvToHost(&u8aRgbInactive, gConfig.u16HueTowerLeft, SATURATION_INACTIVE, VALUE_INACTIVE); + for (i = 0; i < LED_NUM_TOWER; i++) { // GRB - if (gu8DigitalButtons & su8aWingSensors[LED_NUM_WING - i - 1]) { - pWings->aWingL[i].host.r = u8aRgbActive.host.r; - pWings->aWingL[i].host.g = u8aRgbActive.host.g; - pWings->aWingL[i].host.b = u8aRgbActive.host.b; + if (gu8DigitalButtons & su8aTowerSensors[LED_NUM_TOWER - i - 1]) { + pTowers->aTowerL[i].host.r = u8aRgbActive.host.r; + pTowers->aTowerL[i].host.g = u8aRgbActive.host.g; + pTowers->aTowerL[i].host.b = u8aRgbActive.host.b; } else { - pWings->aWingL[i].host.r = u8aRgbInactive.host.r; - pWings->aWingL[i].host.g = u8aRgbInactive.host.g; - pWings->aWingL[i].host.b = u8aRgbInactive.host.b; + pTowers->aTowerL[i].host.r = u8aRgbInactive.host.r; + pTowers->aTowerL[i].host.g = u8aRgbInactive.host.g; + pTowers->aTowerL[i].host.b = u8aRgbInactive.host.b; } } - // Right wing - HsvToHost(&u8aRgbActive, gConfig.u16HueWingRight, SATURATION_ACTIVE, VALUE_ACTIVE); - HsvToHost(&u8aRgbInactive, gConfig.u16HueWingRight, SATURATION_INACTIVE, VALUE_INACTIVE); - for (i = 0; i < LED_NUM_WING; i++) { + // Right tower + HsvToHost(&u8aRgbActive, gConfig.u16HueTowerRight, SATURATION_ACTIVE, VALUE_ACTIVE); + HsvToHost(&u8aRgbInactive, gConfig.u16HueTowerRight, SATURATION_INACTIVE, VALUE_INACTIVE); + for (i = 0; i < LED_NUM_TOWER; i++) { // GRB - if (gu8DigitalButtons & su8aWingSensors[i]) { - pWings->aWingR[i].host.r = u8aRgbActive.host.r; - pWings->aWingR[i].host.g = u8aRgbActive.host.g; - pWings->aWingR[i].host.b = u8aRgbActive.host.b; + if (gu8DigitalButtons & su8aTowerSensors[i]) { + pTowers->aTowerR[i].host.r = u8aRgbActive.host.r; + pTowers->aTowerR[i].host.g = u8aRgbActive.host.g; + pTowers->aTowerR[i].host.b = u8aRgbActive.host.b; } else { - pWings->aWingR[i].host.r = u8aRgbInactive.host.r; - pWings->aWingR[i].host.g = u8aRgbInactive.host.g; - pWings->aWingR[i].host.b = u8aRgbInactive.host.b; + pTowers->aTowerR[i].host.r = u8aRgbInactive.host.r; + pTowers->aTowerR[i].host.g = u8aRgbInactive.host.g; + pTowers->aTowerR[i].host.b = u8aRgbInactive.host.b; } } } @@ -250,24 +380,28 @@ void LED_Ground_Static_RGB(rgb_t* aGround) { } } -void LED_Wings_Controlled_RGB(_led_wings_rgb* pWings) { - // TODO: Get data from game when gbLedDataIsControlledExt (HID, probably) - - for (uint8_t i = 0; i < LED_NUM_WING; i++) { - // The PWM output is wired as BGR on PWM3~5 - pWings->aWingL[i].host.b = gu8IO4PWMOutput[2]; - pWings->aWingR[i].host.b = gu8IO4PWMOutput[2]; - pWings->aWingL[i].host.r = gu8IO4PWMOutput[3]; - pWings->aWingR[i].host.r = gu8IO4PWMOutput[3]; - pWings->aWingL[i].host.g = gu8IO4PWMOutput[4]; - pWings->aWingR[i].host.g = gu8IO4PWMOutput[4]; +void LED_Towers_Controlled_RGB(_led_towers_rgb* pTowers) { + for (uint8_t i = 0; i < LED_NUM_TOWER; i++) { + // The PWM output is wired as BGR on IO4 PWM3~5 + pTowers->aTowerL[i].host.b = gu8IO4PWMOutput[2]; + pTowers->aTowerR[i].host.b = gu8IO4PWMOutput[2]; + pTowers->aTowerL[i].host.r = gu8IO4PWMOutput[3]; + pTowers->aTowerR[i].host.r = gu8IO4PWMOutput[3]; + pTowers->aTowerL[i].host.g = gu8IO4PWMOutput[4]; + pTowers->aTowerR[i].host.g = gu8IO4PWMOutput[4]; } } void LED_Ground_Controlled_RGB(rgb_t* aGround) { // Swap from BRG(game) to GRB(host) for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { +#ifdef LED_CORRECTION_ON_LED_MCU aGround[i].host.r = LED_ScaleU8(gaControlledExtLedData[i].game.r, 255); aGround[i].host.g = LED_ScaleU8(gaControlledExtLedData[i].game.g, 255); aGround[i].host.b = LED_ScaleU8(gaControlledExtLedData[i].game.b, 255); +#else + aGround[i].host.r = LED_ScaleU8(gaControlledExtLedData[i].game.r, 255); + aGround[i].host.g = LED_ScaleU8(gaControlledExtLedData[i].game.g / 2, 255); + aGround[i].host.b = LED_ScaleU8(gaControlledExtLedData[i].game.b / 2, 255); +#endif } } diff --git a/src/led_shared.h b/src/led_shared.h index c821439..d68481c 100644 --- a/src/led_shared.h +++ b/src/led_shared.h @@ -11,6 +11,9 @@ #include "_compiler.h" +// Are we going to be doing global colour correction on the LED MCU (yes!) +#define LED_CORRECTION_ON_LED_MCU + // === LED Firmware Type Configuration === // (Only applicable on the host) #define LED_FW_STOCK 0 @@ -32,9 +35,9 @@ #define LED_NUM_GROUND_LOGICAL 31 // Ground: [0] = Obscured, [1] = Right, [47] = Left #define LED_NUM_GROUND_PHYSICAL 48 -// Right wing: [0] = Bottom, [23] = Top -// Left wing: [23] = Bottom, [0] = Top -#define LED_NUM_WING 24 +// Right tower: [0] = Bottom, [23] = Top +// Left tower: [23] = Bottom, [0] = Top +#define LED_NUM_TOWER 24 // === Colour Byte Order Definitions === // What we know and love @@ -103,19 +106,19 @@ typedef struct __packed { // === HOST->LED MCU Packets === typedef struct __packed { - rgb_t aWingL[LED_NUM_WING]; - rgb_t aWingR[LED_NUM_WING]; -} _led_wings_rgb; + rgb_t aTowerL[LED_NUM_TOWER]; + rgb_t aTowerR[LED_NUM_TOWER]; +} _led_towers_rgb; typedef struct __packed { - hsv_t aWingL[LED_NUM_WING]; - hsv_t aWingR[LED_NUM_WING]; -} _led_wings_hsv; + hsv_t aTowerL[LED_NUM_TOWER]; + hsv_t aTowerR[LED_NUM_TOWER]; +} _led_towers_hsv; typedef struct __packed { uint8_t u8Cmd; uint32_t u32Ground; - uint8_t u8WingFill; + uint8_t u8TowerFill; #if LED_FIRMWARE_TYPE == LED_FW_MAINLAND uint8_t u8Rainbow; uint8_t u8Rsv07; @@ -137,31 +140,31 @@ typedef struct __packed { uint8_t u8Config; #endif rgb_t aGround[LED_NUM_GROUND_LOGICAL]; - _led_wings_rgb Wings; + _led_towers_rgb Towers; } led_rx_full, *Pled_rx_full; typedef struct __packed { uint8_t u8Cmd; uint8_t u8GroundBrightness; - uint8_t u8WingBrightness; + uint8_t u8TowerBrightness; rgb_t aGround[LED_NUM_GROUND_LOGICAL]; - _led_wings_rgb Wings; + _led_towers_rgb Towers; } led_rx_custom_rgb, *Pled_rx_custom_rgb; typedef struct __packed { uint8_t u8Cmd; uint8_t u8GroundBrightness; - uint8_t u8WingBrightness; + uint8_t u8TowerBrightness; rgb_t aGround[LED_NUM_GROUND_LOGICAL]; - _led_wings_hsv Wings; + _led_towers_hsv Towers; } led_rx_custom_mixed, *Pled_rx_custom_mixed; typedef struct __packed { uint8_t u8Cmd; uint8_t u8GroundBrightness; - uint8_t u8WingBrightness; + uint8_t u8TowerBrightness; hsv_t aGround[LED_NUM_GROUND_LOGICAL]; - _led_wings_hsv Wings; + _led_towers_hsv Towers; } led_rx_custom_hsv, *Pled_rx_custom_hsv; typedef struct __packed { @@ -209,5 +212,4 @@ typedef struct __packed { #define LED_DIVIDER_14_15 29 // === HSV Definitions === -#define LED_HUE_SCALE 5 // For transmission to LED board in basic mode #define LED_HUE_MAX 360 diff --git a/src/main.c b/src/main.c index 752099c..3989555 100644 --- a/src/main.c +++ b/src/main.c @@ -70,6 +70,10 @@ void Digital_TickInputs() { int _entry(void) { SYS_UnlockReg(); + + // Load our configuration, so we know if the LED processor should be rebooted + FMC_EEPROM_Load(); + SYS_Init(); #ifdef ENABLE_BOOTLOADER_CHECK SYS_Bootloader_Check(); @@ -77,8 +81,6 @@ int _entry(void) { SYS_ModuleInit(); // TODO: Re-lock registers, ideally. Need to check which registers we use where - FMC_EEPROM_Load(); - gu8VComReady = 1; gu8HIDIO4Ready = 1; gu8HIDMiscReady = 1; @@ -108,11 +110,10 @@ int _entry(void) { if (bPSoCDirtyVolatile) { bPSoCDirtyVolatile = 0; PSoC_PostProcessing(); - - if (!bPSoCHasTalked) { - PSoC_SetFingerCapacitanceFromConfig(1); - bPSoCHasTalked = 1; - } + } + if (bPSoCAliveVolatile && !bPSoCHasTalked) { + PSoC_SetFingerCapacitanceFromConfig(1); + bPSoCHasTalked = 1; } Slider_TickSerial(); diff --git a/src/pins.h b/src/pins.h index 33507a3..6ff0db4 100644 --- a/src/pins.h +++ b/src/pins.h @@ -10,7 +10,7 @@ #define PIN_RX2 PD9 #define PIN_RX3 PD10 #define PIN_RX4 PD11 -#define PIN_LED_WING_PWR PB13 +#define PIN_LED_TOWER_PWR PB13 #define PIN_LED_GROUND_PWR PC3 #define _PIN_SDA PA, BIT10 @@ -25,7 +25,7 @@ #define _PIN_RX2 PD, BIT9 #define _PIN_RX3 PD, BIT10 #define _PIN_RX4 PD, BIT11 -#define _PIN_LED_WING_PWR PB, BIT13 +#define _PIN_LED_TOWER_PWR PB, BIT13 #define _PIN_LED_GROUND_PWR PC, BIT3 #define PIN_AIR1 PIN_RX2 diff --git a/src/psoc.c b/src/psoc.c index 8afb4c2..8d4fe2a 100644 --- a/src/psoc.c +++ b/src/psoc.c @@ -6,6 +6,9 @@ // #pragma GCC push_options // #pragma GCC optimize ("O0") +// TODO: Anything higher than O1 is breaking this code. Probably something hasn't been marked as +// volatile that should be! + uint16_t gu16PSoCDiff[32] = { 0 }; // Touch threshold as calculated by SmartSense. Has hysteresis built in. // 0 is an insane default, so instead we default to 100 which is far less likely to break things :) @@ -27,9 +30,10 @@ uint32_t gu32LastCapSenseStart = 0; uint32_t gu32LastCapSenseEnd = 0; volatile uint8_t bPSoCDirtyVolatile = 0; +volatile uint8_t bPSoCAliveVolatile = 0; volatile uint8_t bForceSliderSend = 0; -static void* pu8PsocRxDestination = NULL; +static volatile void* pu8PsocRxDestination = NULL; static uint8_t pu8PsocRxDestinationLen = 0; static volatile uint8_t* volatile pu8PsocGotData = NULL; static volatile PSoC_CMD_RX eBlockingCommand = _PSoC_CMD_RX_NONE; @@ -41,9 +45,8 @@ static void PSoC_HandleRx(PSoC_CMD_RX eCmd, uint8_t u8Len, uint8_t* u8Data) { } switch (eCmd) { - case PSoC_CMD_RX_REQUEST_FINGER_CAP: - // We're in the IRQ here, so don't block on this! - PSoC_SetFingerCapacitanceFromConfig(0); + case PSoC_CMD_RX_INITIALISATION_COMPLETE: + bPSoCAliveVolatile = 1; return; // Debug traces @@ -64,6 +67,7 @@ static void PSoC_HandleRx(PSoC_CMD_RX eCmd, uint8_t u8Len, uint8_t* u8Data) { gu16PSoCDiff[16 + i] |= u8Data[(i * 2) + 1]; } bPSoCDirtyVolatile = 1; + bPSoCAliveVolatile = 1; return; case PSoC_CMD_RX_MASTER_DIFF: @@ -76,6 +80,7 @@ static void PSoC_HandleRx(PSoC_CMD_RX eCmd, uint8_t u8Len, uint8_t* u8Data) { gu16PSoCDiff[i] |= u8Data[(i * 2) + 1]; } bPSoCDirtyVolatile = 1; + bPSoCAliveVolatile = 1; return; // Arbitrary data reception @@ -112,7 +117,7 @@ static void PSoC_HandleRx(PSoC_CMD_RX eCmd, uint8_t u8Len, uint8_t* u8Data) { if (pu8PsocRxDestination) { if (u8Len > pu8PsocRxDestinationLen) u8Len = pu8PsocRxDestinationLen; - memcpy(pu8PsocRxDestination, u8Data, u8Len); + memcpy((void*)pu8PsocRxDestination, u8Data, u8Len); // Make sure we don't go clobbering stuff later! pu8PsocRxDestination = NULL; } @@ -164,9 +169,12 @@ static uint8_t PSoC_Valid_Len(PSoC_CMD_RX eCmd, uint8_t u8Len) { // We should never be seeing a length for these! return 0; + // TODO: This doesn't seem to be a packet! + case PSoC_CMD_RX_INITIALISATION_COMPLETE: + return 0; + // Commands that have a payload we need to receive case PSoC_CMD_RX_SET_FINGER_CAP: - case PSoC_CMD_RX_REQUEST_FINGER_CAP: case PSoC_CMD_RX_ENABLE_DEBUG: return u8Len == 2; case PSoC_CMD_RX_MASTER_DIFF: @@ -221,10 +229,12 @@ void UART1_IRQHandler(void) { case PSoC_CMD_RX_CS_END: // PSoC_HandleRx(u8Data, 0, NULL); return; + case PSoC_CMD_RX_INITIALISATION_COMPLETE: + PSoC_HandleRx(u8Data, 0, NULL); + return; // Commands that have a payload we need to receive case PSoC_CMD_RX_SET_FINGER_CAP: - case PSoC_CMD_RX_REQUEST_FINGER_CAP: case PSoC_CMD_RX_MASTER_DIFF: case PSoC_CMD_RX_SLAVE_DIFF: case PSoC_CMD_RX_MASTER_TOUCH_TH: @@ -325,8 +335,7 @@ static inline void PSoC_Cmd(PSoC_CMD_TX eCmd, uint8_t u8D0, uint8_t u8D1, PSoC_C } uint16_t PSoC_GetFingerCapacitance(void) { - uint16_t u16FingerCap; - + static volatile uint16_t u16FingerCap = 0; pu8PsocRxDestination = &u16FingerCap; pu8PsocRxDestinationLen = sizeof u16FingerCap; PSoC_Cmd(PSoC_CMD_TX_GET_FINGER_CAP, 0, 0, PSoC_CMD_RX_GET_FINGER_CAP); diff --git a/src/psoc.h b/src/psoc.h index 515aae4..56ba984 100644 --- a/src/psoc.h +++ b/src/psoc.h @@ -28,7 +28,7 @@ typedef enum : uint8_t { // PSoC -> Host PSoC_CMD_RX_MASTER_DIFF = 0xAD, PSoC_CMD_RX_SLAVE_DIFF = 0xAF, - PSoC_CMD_RX_REQUEST_FINGER_CAP = 0xC1, + PSoC_CMD_RX_INITIALISATION_COMPLETE = 0xC1, // PSoC -> Host (Debug) PSoC_CMD_RX_MASTER_TOUCH_TH = 0xAA, @@ -59,6 +59,8 @@ extern uint16_t gu16PSoCDiff[32]; // Has the difference data changed in the interrupt handler? extern volatile uint8_t bPSoCDirtyVolatile; +// Have we received indication that the PSoC is ready for packets? +extern volatile uint8_t bPSoCAliveVolatile; // Has the post-processed difference data changed? extern volatile uint8_t bForceSliderSend; diff --git a/src/slider.c b/src/slider.c index 490f095..9ae5f3f 100644 --- a/src/slider.c +++ b/src/slider.c @@ -229,12 +229,14 @@ static void Slider_Process(slider_cmd_Rx u8SliderCmd, uint8_t* pu8Packet, uint8_ break; case SLIDER_DEBUG_CMD_Rx_HOST_ENTER_LDROM: + // Remember if we're going to want to kick the LED board into LDROM next reboot + gConfig.u8NextBootLEDBootloader = pu8Packet[1]; + bConfigDirty = 1; + FMC_EEPROM_Store(); + SYS_EnterLDROM(); break; case SLIDER_DEBUG_CMD_Rx_LED_ENTER_LDROM: - gu8LEDTx[0] = LED_CMD_FMC_ENTER_LDROM; - // The LED firmware checks every 10ms or so - CLK_SysTickLongDelay(15 ms); SYS_WaitBootloaderLED(); break; case SLIDER_DEBUG_CMD_Rx_LED_CHECK: @@ -242,7 +244,7 @@ static void Slider_Process(slider_cmd_Rx u8SliderCmd, uint8_t* pu8Packet, uint8_ sizeof gbLedIsCustom); break; - case SLIDER_DEBUG_CMD_Rx_LED_GET_DIGITAL: + case SLIDER_DEBUG_CMD_Rx_GET_DIGITAL: Slider_Respond(SLIDER_CMD_Tx_DEBUG, &gu8DigitalButtons, sizeof gu8DigitalButtons); break; @@ -342,8 +344,8 @@ void Slider_TickSerial(void) { void Slider_Tick1ms() { if (gbLedDataIsControlledExt) { - // If we haven't had an LED packet in 5 seconds, call it quits - if (++su32SinceLastControlled == 5 * 1000) gbLedDataIsControlledExt = 0; + // If we haven't had an LED packet in 1 second, call it quits + if (++su32SinceLastControlled == 1000) gbLedDataIsControlledExt = 0; } static uint16_t u16Counter = 0; diff --git a/src/slider.h b/src/slider.h index f849b2b..5f20f6d 100644 --- a/src/slider.h +++ b/src/slider.h @@ -121,7 +121,7 @@ typedef enum : uint8_t { SLIDER_DEBUG_CMD_Rx_LED_CHECK = 0x22, // IO Access - SLIDER_DEBUG_CMD_Rx_LED_GET_DIGITAL = 0x30, + SLIDER_DEBUG_CMD_Rx_GET_DIGITAL = 0x30, } slider_debug_cmd_Rx; typedef enum : uint8_t { SLIDER_CMD_Tx_REPORT = 0x01, diff --git a/src/sys.c b/src/sys.c index 612b6ae..01ae1aa 100644 --- a/src/sys.c +++ b/src/sys.c @@ -75,12 +75,28 @@ void SYS_Init(void) { GPIO_SetMode(_PIN_EC3, GPIO_PMD_INPUT); GPIO_SetMode(_PIN_USB_MUX_SEL, GPIO_PMD_OUTPUT); GPIO_SetMode(_PIN_USB_MUX_EN, GPIO_PMD_OUTPUT); - GPIO_SetMode(_PIN_LED_WING_PWR, GPIO_PMD_OUTPUT); + GPIO_SetMode(_PIN_LED_TOWER_PWR, GPIO_PMD_OUTPUT); GPIO_SetMode(_PIN_LED_GROUND_PWR, GPIO_PMD_OUTPUT); - PIN_LED_WING_PWR = 1; + PIN_LED_TOWER_PWR = 1; PIN_LED_GROUND_PWR = 1; + if (gConfig.u8NextBootLEDBootloader) { + // If we've been instructed to, immediately reboot the LED module into LDROM + + gConfig.u8NextBootLEDBootloader = 0; + bConfigDirty = 1; + FMC_EEPROM_Store(); + + // Switch PA10 and PA11 to GPIO so we can pull them low + SYS->GPA_MFP &= ~(SYS_GPA_MFP_PA10_Msk | SYS_GPA_MFP_PA11_Msk); + SYS->GPA_MFP |= (SYS_GPA_MFP_PA10_GPIO | SYS_GPA_MFP_PA11_GPIO); + SYS->ALT_MFP &= ~(SYS_ALT_MFP_PA10_Msk | SYS_ALT_MFP_PA11_Msk); + SYS->ALT_MFP |= (SYS_ALT_MFP_PA10_GPIO | SYS_ALT_MFP_PA11_GPIO); + + SYS_WaitBootloaderLED(); + } + // If FN2 is depressed, trigger the LED bootloader to enter bootloading mode by pulling both // PA10 and PA11 low rather than configuring them for I2C (pulling high). // @@ -95,6 +111,10 @@ void SYS_Init(void) { GPIO_SetMode(_PIN_SCL, GPIO_PMD_OUTPUT); PIN_SDA = 0; PIN_SCL = 0; + + // TODO: Nicer way of doing this for the automated firmware process? + // while (1) + // ; } else { // Set GPA multi-function pins for I2C1 SDA and SCL SYS->GPA_MFP &= ~(SYS_GPA_MFP_PA10_Msk | SYS_GPA_MFP_PA11_Msk); @@ -208,6 +228,13 @@ void TMR0_IRQHandler(void) { void __attribute__((noreturn)) SYS_EnterLDROM(void) { SYS_UnlockReg(); + // Turn off our USB PHY + USBD->ATTR = 0x650; + NVIC_DisableIRQ(USBD_IRQn); + SYS_ResetModule(USBD_RST); + // Give Windows a moment to notice the disconnection + CLK_SysTickLongDelay(1000 ms); + // If we use a CPU reset, I2C is left setup and so the LED board will be timing out rather than // early-NACKS. // If we use a CHIP reset we aren't guaranteed to land in LDROM because it's based on the CONFIG @@ -255,7 +282,7 @@ void SYS_WaitBootloaderLED(void) { NVIC_DisableIRQ(USBD_IRQn); SYS_ResetModule(USBD_RST); // Give Windows a moment to notice the disconnection - CLK_SysTickLongDelay(1000 ms); + CLK_SysTickLongDelay(500 ms); // Switch the USB connection over the LED microcontroller PIN_USB_MUX_SEL = USB_MUX_LEDS; PIN_USB_MUX_EN = USB_MUX_ENABLE; @@ -287,4 +314,4 @@ void SYS_WaitBootloaderLED(void) { Tas_USBD_Init(); Tas_USBD_Start(); NVIC_EnableIRQ(USBD_IRQn); -} \ No newline at end of file +} diff --git a/src/tasoller.h b/src/tasoller.h index 470b933..0204c6a 100644 --- a/src/tasoller.h +++ b/src/tasoller.h @@ -14,6 +14,9 @@ extern uint8_t gbUIOpen; #define BYTESWAP_U16(x) ((((x) & 0xFF) << 8) | ((x) >> 8)) +// === Touch input === +// #define ENABLE_TOUCH_INPUT + // === DAO-DRM === // * For now the bootloader check is being left enabled. // * It'll help catch if I break that in my bootloader :P diff --git a/src/timestamp.c b/src/timestamp.c new file mode 100644 index 0000000..ce0587d --- /dev/null +++ b/src/timestamp.c @@ -0,0 +1,2 @@ +__attribute__((section(".compile_timestamp"))) const char compile_time[] __attribute__((used)) = + "HOST" __DATE__ " " __TIME__; diff --git a/src/ui.c b/src/ui.c index c16e902..14bb572 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1,5 +1,6 @@ #include "tasoller.h" +#define FN1_TAP_TIME 250 // ms of FN1 to enter config #define FN1_HOLD_TIME 250 // ms of FN1 to enter config #define FN2_TAP_TIME 250 // ms of FN2 to enter service/test #define FN2_HOLD_TIME 500 // ms of FN2 to enter service/test @@ -16,12 +17,18 @@ uint8_t gbUIOpen = 0; static uint8_t u8TestIsActive = 0; -static void UI_TickSensitivity(void) { - for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { - gaControlledIntLedData[i].h = 0; - gaControlledIntLedData[i].s = (gConfig.u8Sens - 1) * 16; - gaControlledIntLedData[i].v = 0; +static inline void UI_WriteRange(const uint8_t u8Start, const uint8_t u8End, const uint16_t u16H, + const uint8_t u8S, const uint8_t u8V) { + if (u8End < u8Start) return; + for (uint8_t i = u8Start; i <= u8End; i++) { + gaControlledIntLedData[i].h = u16H; + gaControlledIntLedData[i].s = u8S; + gaControlledIntLedData[i].v = u8V; } +} + +static void UI_TickSensitivity(void) { + UI_WriteRange(0, LED_NUM_GROUND_LOGICAL - 1, 0, (gConfig.u8Sens - 1) * 16, 0); for (uint8_t i = 0; i < gConfig.u8Sens; i++) gaControlledIntLedData[i * 2].v = 255; @@ -57,14 +64,10 @@ static void UI_TickSettings(void) { if (++u8Pulser == 255) u8PulserDir = 1; } - for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { - gaControlledIntLedData[i].h = 0; - gaControlledIntLedData[i].s = 255; - gaControlledIntLedData[i].v = 0; - } + UI_WriteRange(0, LED_NUM_GROUND_LOGICAL - 1, 0, 255, 0); { // LED colour control - gaControlledIntLedData[LED_CELL_0].h = gConfig.u16HueWingLeft; + gaControlledIntLedData[LED_CELL_0].h = gConfig.u16HueTowerLeft; gaControlledIntLedData[LED_CELL_0].v = 255; gaControlledIntLedData[LED_CELL_1].h = gConfig.u16HueGround; gaControlledIntLedData[LED_CELL_1].v = 255; @@ -72,17 +75,17 @@ static void UI_TickSettings(void) { gaControlledIntLedData[LED_DIVIDER_1_2].v = 255; gaControlledIntLedData[LED_CELL_2].h = gConfig.u16HueGround; gaControlledIntLedData[LED_CELL_2].v = 255; - gaControlledIntLedData[LED_CELL_3].h = gConfig.u16HueWingRight; + gaControlledIntLedData[LED_CELL_3].h = gConfig.u16HueTowerRight; gaControlledIntLedData[LED_CELL_3].v = 255; - if (gu32PSoCDigitalTrig & PAD_1_Msk) MOD_INCR(gConfig.u16HueWingLeft, LED_HUE_MAX); - if (gu32PSoCDigitalTrig & PAD_2_Msk) MOD_DECR(gConfig.u16HueWingLeft, LED_HUE_MAX); + if (gu32PSoCDigitalTrig & PAD_1_Msk) MOD_INCR(gConfig.u16HueTowerLeft, LED_HUE_MAX); + if (gu32PSoCDigitalTrig & PAD_2_Msk) MOD_DECR(gConfig.u16HueTowerLeft, LED_HUE_MAX); if (gu32PSoCDigitalTrig & PAD_3_Msk) MOD_INCR(gConfig.u16HueGround, LED_HUE_MAX); if (gu32PSoCDigitalTrig & PAD_4_Msk) MOD_DECR(gConfig.u16HueGround, LED_HUE_MAX); if (gu32PSoCDigitalTrig & PAD_5_Msk) MOD_INCR(gConfig.u16HueGroundActive, LED_HUE_MAX); if (gu32PSoCDigitalTrig & PAD_6_Msk) MOD_DECR(gConfig.u16HueGroundActive, LED_HUE_MAX); - if (gu32PSoCDigitalTrig & PAD_7_Msk) MOD_INCR(gConfig.u16HueWingRight, LED_HUE_MAX); - if (gu32PSoCDigitalTrig & PAD_8_Msk) MOD_DECR(gConfig.u16HueWingRight, LED_HUE_MAX); + if (gu32PSoCDigitalTrig & PAD_7_Msk) MOD_INCR(gConfig.u16HueTowerRight, LED_HUE_MAX); + if (gu32PSoCDigitalTrig & PAD_8_Msk) MOD_DECR(gConfig.u16HueTowerRight, LED_HUE_MAX); } // [Cell 4 no function] { // Lighting toggles @@ -105,17 +108,17 @@ static void UI_TickSettings(void) { } else { gaControlledIntLedData[LED_CELL_6].v = 255; } - if (gConfig.u8LedWingBrightness) { + if (gConfig.u8LedTowerBrightness) { gaControlledIntLedData[LED_CELL_7].s = 0; - gaControlledIntLedData[LED_CELL_7].v = gConfig.u8LedWingBrightness; + gaControlledIntLedData[LED_CELL_7].v = gConfig.u8LedTowerBrightness; } else { gaControlledIntLedData[LED_CELL_7].v = 255; } if (gu32PSoCDigitalTrig & PAD_13_Msk) INCR(gConfig.u8LedGroundBrightness, 255); if (gu32PSoCDigitalTrig & PAD_14_Msk) DECR(gConfig.u8LedGroundBrightness, 0); - if (gu32PSoCDigitalTrig & PAD_15_Msk) INCR(gConfig.u8LedWingBrightness, 255); - if (gu32PSoCDigitalTrig & PAD_16_Msk) DECR(gConfig.u8LedWingBrightness, 0); + if (gu32PSoCDigitalTrig & PAD_15_Msk) INCR(gConfig.u8LedTowerBrightness, 255); + if (gu32PSoCDigitalTrig & PAD_16_Msk) DECR(gConfig.u8LedTowerBrightness, 0); } // [Cell 8 no function] @@ -162,11 +165,7 @@ static uint32_t su32EnteredTestMenuAt = 0; static void UI_TickServiceTest(void) { uint8_t u8V = 0; // Zero out the LED data - for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { - gaControlledIntLedData[i].h = 0; - gaControlledIntLedData[i].s = 0; - gaControlledIntLedData[i].v = 0; - } + UI_WriteRange(0, LED_NUM_GROUND_LOGICAL - 1, 0, 0, 0); uint8_t u8ForceTest = 0; if (u8TestIsActive) { @@ -183,11 +182,8 @@ static void UI_TickServiceTest(void) { } else { u8V = 50; } - gaControlledIntLedData[LED_CELL_0].v = u8V; - gaControlledIntLedData[LED_DIVIDER_0_1].v = u8V; - gaControlledIntLedData[LED_CELL_1].v = u8V; - gaControlledIntLedData[LED_DIVIDER_1_2].v = u8V; - gaControlledIntLedData[LED_CELL_2].v = u8V; + + UI_WriteRange(LED_CELL_0, LED_CELL_2, 0, 0, u8V); } gaControlledIntLedData[LED_DIVIDER_2_3].v = 255; { // Up @@ -196,11 +192,7 @@ static void UI_TickServiceTest(void) { } else { u8V = 50; } - gaControlledIntLedData[LED_CELL_3].v = u8V; - gaControlledIntLedData[LED_DIVIDER_3_4].v = u8V; - gaControlledIntLedData[LED_CELL_4].v = u8V; - gaControlledIntLedData[LED_DIVIDER_4_5].v = u8V; - gaControlledIntLedData[LED_CELL_5].v = u8V; + UI_WriteRange(LED_CELL_3, LED_CELL_5, 0, 0, u8V); } gaControlledIntLedData[LED_DIVIDER_5_6].v = 255; @@ -211,11 +203,7 @@ static void UI_TickServiceTest(void) { } else { u8V = 50; } - gaControlledIntLedData[LED_CELL_13].v = u8V; - gaControlledIntLedData[LED_DIVIDER_13_14].v = u8V; - gaControlledIntLedData[LED_CELL_14].v = u8V; - gaControlledIntLedData[LED_DIVIDER_14_15].v = u8V; - gaControlledIntLedData[LED_CELL_15].v = u8V; + UI_WriteRange(LED_CELL_13, LED_CELL_15, 0, 255, u8V); } } @@ -250,34 +238,54 @@ static void UI_TickServiceTest(void) { gaControlledIntLedData[LED_DIVIDER_9_10].v = 255; } +static uint8_t u8Fn1Held = 0; +static inline void _UI_SettingsOnExit(void) { + // If FN2 was released while still in sensitivity adjustment, make sure the changes save + if (su8SensTimeout) { + PSoC_SetFingerCapacitanceFromConfig(1); + su8SensTimeout = 0; + } + + bConfigDirty = 1; + + u16RequestedConsumerControl = 0; + u32EnterPressStarted = 0; +} + void UI_Tick(void) { + static uint8_t u8LastDB = 0; + const uint8_t u8PosDb = gu8DigitalButtons & (~u8LastDB); + + static uint8_t u8ConfigIsActive = 0; + + // Handle double tap trigger on FN1 + static uint32_t u32LastFn1 = 0; + if (u8PosDb & DIGITAL_FN1_Msk) { + if (u32LastFn1 && MS_SINCE(u32LastFn1) < FN1_TAP_TIME) { + u8ConfigIsActive = !u8ConfigIsActive; + + if (!u8ConfigIsActive) { + _UI_SettingsOnExit(); + } + } + u32LastFn1 = gu32NowMs; + } + // Handle hold trigger on FN1 - static uint8_t u8Fn1Held = 0; if (gu8DigitalButtons & DIGITAL_FN1_Msk) { if (u8Fn1Held < FN1_HOLD_TIME) u8Fn1Held++; } else { // We released the button after holding it for long enough to be in the configuration UI, so // assume something changed if (u8Fn1Held >= FN1_HOLD_TIME) { - // If FN2 was released while still in sensitivity adjustment, make sure the changes save - if (su8SensTimeout) { - PSoC_SetFingerCapacitanceFromConfig(1); - su8SensTimeout = 0; - } - - bConfigDirty = 1; + _UI_SettingsOnExit(); } - - u16RequestedConsumerControl = 0; - u32EnterPressStarted = 0; u8Fn1Held = 0; } // Handle double tap trigger on FN2 static uint32_t u32LastFn2 = 0; - static uint8_t u8LastDB = 0; - const uint8_t u8PostDb = gu8DigitalButtons & (~u8LastDB); - if (u8PostDb & DIGITAL_FN2_Msk) { + if (u8PosDb & DIGITAL_FN2_Msk) { if (u32LastFn2 && MS_SINCE(u32LastFn2) < FN2_TAP_TIME) { u8TestIsActive = !u8TestIsActive; @@ -291,7 +299,6 @@ void UI_Tick(void) { } u32LastFn2 = gu32NowMs; } - u8LastDB = gu8DigitalButtons; // Handle hold trigger on FN2 static uint16_t u16Fn2Held = 0; @@ -301,8 +308,11 @@ void UI_Tick(void) { u16Fn2Held = 0; } + // Persistent state + u8LastDB = gu8DigitalButtons; + // Render the appropriate UI based on what's being done - if (u8Fn1Held >= FN1_HOLD_TIME) { + if (u8Fn1Held >= FN1_HOLD_TIME || u8ConfigIsActive) { gbLedDataIsControlledInt = 1; gbUIOpen = 1; UI_TickSettings(); diff --git a/src/usb_def.h b/src/usb_def.h index 2442029..c87d1d6 100644 --- a/src/usb_def.h +++ b/src/usb_def.h @@ -55,23 +55,28 @@ enum : uint8_t { #define USBD_CDC_CMD_MAX_SIZE (16) #define USBD_CDC_IN_MAX_SIZE (64) // Device -> Host #define USBD_CDC_OUT_MAX_SIZE (64) // Host -> Device -#define USBD_HID_BUF_LEN (64) +#define USBD_HID_BUF_LEN_IO4 (64) +#define USBD_HID_BUF_LEN_IN (64) // 104 +#define USBD_HID_BUF_LEN_OUT (64) -_Static_assert(USBD_HID_BUF_LEN >= sizeof(hid_kbd_report_t) && - USBD_HID_BUF_LEN >= sizeof(hid_consumer_report_t) && - USBD_HID_BUF_LEN >= sizeof(io4_hid_in_t) && - USBD_HID_BUF_LEN >= sizeof(io4_hid_out_t), +_Static_assert(USBD_HID_BUF_LEN_IN >= sizeof(hid_kbd_report_t) && + USBD_HID_BUF_LEN_IN >= sizeof(hid_consumer_report_t) && + USBD_HID_BUF_LEN_IN >= sizeof(hid_enter_report_t) && + USBD_HID_BUF_LEN_IO4 >= sizeof(io4_hid_in_t) && + USBD_HID_BUF_LEN_IO4 >= sizeof(io4_hid_out_t), "HID USB buffer insufficient size for possible reports"); -// Endpoint packet max size (cannot total more than 512!) -#define EP0_MAX_PKT_SIZE 64 -#define EP1_MAX_PKT_SIZE 64 -#define EP2_MAX_PKT_SIZE USBD_CDC_IN_MAX_SIZE -#define EP3_MAX_PKT_SIZE USBD_CDC_OUT_MAX_SIZE -#define EP4_MAX_PKT_SIZE USBD_CDC_CMD_MAX_SIZE -#define EP5_MAX_PKT_SIZE USBD_HID_BUF_LEN -#define EP6_MAX_PKT_SIZE USBD_HID_BUF_LEN -#define EP7_MAX_PKT_SIZE USBD_HID_BUF_LEN +// Endpoint packet max size (cannot total more than 504!) +// Control must be 64 bytes because we're receiving IO4 OUT over EP1 +#define EP0_MAX_PKT_SIZE 64 // Control | device->host +#define EP1_MAX_PKT_SIZE 64 // Control | host ->device +#define EP2_MAX_PKT_SIZE USBD_CDC_IN_MAX_SIZE // CDC | device->host +#define EP3_MAX_PKT_SIZE USBD_CDC_OUT_MAX_SIZE // CDC | host ->device +#define EP4_MAX_PKT_SIZE USBD_CDC_CMD_MAX_SIZE // CDC command | device->host (hmm?) +#define EP5_MAX_PKT_SIZE USBD_HID_BUF_LEN_IO4 // IO4 HID | device->host +#define EP6_MAX_PKT_SIZE USBD_HID_BUF_LEN_IN // Misc HID | device->host +#define EP7_MAX_PKT_SIZE USBD_HID_BUF_LEN_OUT // Misc HID | host ->device +// TODO: Do we need host->device HID? We could save quite a few bytes and an endpoint! #define SETUP_BUF_BASE 0 #define SETUP_BUF_LEN 8 diff --git a/src/usb_inc/hid.h b/src/usb_inc/hid.h index c8bed22..542fa6e 100644 --- a/src/usb_inc/hid.h +++ b/src/usb_inc/hid.h @@ -1341,6 +1341,12 @@ #define HID_USAGE_BARREL_SWITCH 1, 0x44 #define HID_USAGE_ERASER 1, 0x45 #define HID_USAGE_TABLET_PICK 1, 0x46 +#define HID_USAGE_CONFIDENCE 1, 0x47 +#define HID_USAGE_WIDTH 1, 0x48 +#define HID_USAGE_HEIGHT 1, 0x49 +#define HID_USAGE_CONTACT_IDENTIFIER 1, 0x51 +#define HID_USAGE_CONTACT_COUNT 1, 0x54 +#define HID_USAGE_SCAN_TIME 1, 0x56 /* Alphanumeric Display Usages */ #define HID_USAGE_ALPHANUMERIC_DISPLAY 1, 0x01 diff --git a/src/usbd_driver.c b/src/usbd_driver.c index 9fa0943..f7e4bed 100644 --- a/src/usbd_driver.c +++ b/src/usbd_driver.c @@ -55,12 +55,12 @@ void Tas_USBD_Init(void) { USBD_CONFIG_EP(EP_HID_IO4_IN, USBD_CFG_EPMODE_IN | 4); USBD_SET_EP_BUF_ADDR(EP_HID_IO4_IN, EP5_BUF_BASE); - // Misc HID IN/OUT on EP6/EP6 + // Misc HID IN/OUT on EP6/EP7 USBD_CONFIG_EP(EP_HID_MISC_IN, USBD_CFG_EPMODE_IN | 5); USBD_SET_EP_BUF_ADDR(EP_HID_MISC_IN, EP6_BUF_BASE); USBD_CONFIG_EP(EP_HID_MISC_OUT, USBD_CFG_EPMODE_OUT | 6); USBD_SET_EP_BUF_ADDR(EP_HID_MISC_OUT, EP7_BUF_BASE); - USBD_SET_PAYLOAD_LEN(EP_HID_MISC_OUT, USBD_HID_BUF_LEN); + USBD_SET_PAYLOAD_LEN(EP_HID_MISC_OUT, USBD_HID_BUF_LEN_OUT); } void Tas_USBD_Start(void) { // 100ms delay required as part of spec