Damn thats a lot

This commit is contained in:
Bottersnike 2024-08-04 22:27:46 +01:00
parent 923886efde
commit 8718c10f03
No known key found for this signature in database
45 changed files with 4903 additions and 1521 deletions

3
.gitignore vendored
View File

@ -5,3 +5,6 @@ picolibc/*
# Build artifacts
obj/*
host_aprom.bin
# Flashing tool
flashtool/*

View File

@ -2,6 +2,7 @@
"recommendations": [
"aaron-bond.better-comments",
"marus25.cortex-debug",
"llvm-vs-code-extensions.vscode-clangd"
"llvm-vs-code-extensions.vscode-clangd",
"aaron-bond.better-comments"
]
}

63
.vscode/settings.json vendored
View File

@ -6,13 +6,70 @@
"-I${workspaceFolder}/NUC123/inc",
"-I${workspaceFolder}/NUC123/StdDriver/inc",
"-D__GNUC__",
"-D__XSTRING(x)=", // Workaround for string.h
"-D__XSTRING(x)=", // Workaround for string.h
"-Wno-pointer-to-int-cast",
"-Wno-int-to-pointer-cast",
"-Wno-int-to-pointer-cast"
],
"cortex-debug.variableUseNaturalFormat": true,
"cortex-debug.variableUseNaturalFormat": false,
"cortex-debug.liveWatchRefreshRate": 1000,
"cortex-debug.enableTelemetry": false,
"files.trimFinalNewlines": true,
"editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd",
"better-comments.tags": [
{
"tag": "===",
"color": "#00F5FF",
"strikethrough": false,
"underline": false,
"backgroundColor": "#00F5FF11",
"bold": false,
"italic": false
},
{
"tag": "!",
"color": "#FF2D00",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "?",
"color": "#3498DB",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "//",
"color": "#474747",
"strikethrough": true,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "todo",
"color": "#FF8C00",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "*",
"color": "#98C379",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
}
]
}

View File

@ -1,27 +0,0 @@
# Development
## Requirements
- Windows (Strongly recommend. Compiling on anything else is not officially supported.)
- [Make for Windows](https://gnuwin32.sourceforge.net/packages/make.htm)
- On the path, please
- [Arm GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) (Currently using 13.2 Rel1)
- To make your life easier, ensure this is located at `C:\Program Files (x86)\GNU Arm Embedded Toolchain\13.2 Rel1`
- [picolibc 1.8.6](https://github.com/picolibc/picolibc/releases/download/1.8.6/picolibc-1.8.6-13.2.Rel1.zip)
- Place the contents of this zip file in `picolibc`
VSCode is recommended for development, and all recommended extensions should be installed.
## Compiling and Uploading
Run `make -j` to compile the firmware.
For rapid development, the HID bootloader is quite annoying. `flash.cmd` is provided which reprograms the chip using an ST-Link V2. This requires an OpenOCD installation in the root project folder, along with an existing compiled host bootloader binary.
For debugging in VSCode, install the recommended extensions then hit F5. This requires an OpenOCD installation in the root project folder, and an ST-Link V2.
## NUC123 BSP
The `NUC123` folder is a heavily reduced form of the complete BSP provided by Nuvoton. Modifications should not be made to any files within the `inc` or `StdDriver` folder.
`startup_NUC123.s` is the program entrypoint, and may require modification.
`NUC123.ld` is a modified version of the GCC linker script provided by Nuvoton, and may require modification.
To pull in additional BSP drivers, if required, the `Makefile` should be modified.

2
GCC.mk
View File

@ -16,6 +16,6 @@ OPTIM ?= -flto -Os
GCCFLAGS := $(SPECS) -nolibc -nostdlib -nostartfiles -nodefaultlibs -mcpu=cortex-m0 -mthumb -Wl,--gc-sections $(OPTIM) -g
CFLAGS := $(GCCFLAGS) -c -ffunction-sections -fdata-sections -fsigned-char \
-fmessage-length=0 -ffreestanding
-fmessage-length=0 -ffreestanding -fstack-usage -Wall
ASFLAGS := $(GCCFLAGS) -c -x assembler-with-cpp
LDFLAGS := $(GCCFLAGS) -Lpicolibc/arm-none-eabi/picolibc/arm-none-eabi/lib/thumb/v6-m/nofp

View File

@ -3,12 +3,9 @@ OBJ_DIR := obj
BINARY_NAME := host_aprom
OPTIM := -O0
# OPTIM := -Ofast -flto
# OPTIM := -Os -flto
include GCC.mk
LIBRARY_MODULES := clk uart timer i2c
include NUC123/NUC123.mk
CFLAGS += -Wno-gnu-variable-sized-type-not-at-end
include generic.mk

View File

@ -1,6 +1,5 @@
/* Linker script to configure memory regions. */
MEMORY
{
MEMORY {
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x10000 /* 64k */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x5000 /* 20k */
}
@ -8,127 +7,45 @@ MEMORY
/* Library configurations */
GROUP(libgcc.a libc.a libm.a libnosys.a)
/* Linker script to place sections and symbol values. Should be used together
* with other linker script that defines memory regions FLASH and RAM.
* It references following symbols, which must be defined in code:
* Reset_Handler : Entry of reset handler
*
* It defines following symbols, which code can use without definition:
* __exidx_start
* __exidx_end
* __copy_table_start__
* __copy_table_end__
* __zero_table_start__
* __zero_table_end__
/**
* Used for loading .data into memory:
* __etext
* __data_start__
* __preinit_array_start
* __preinit_array_end
* __init_array_start
* __init_array_end
* __fini_array_start
* __fini_array_end
* __data_end__
* Used for zeroing .bss:
* __bss_start__
* __bss_end__
* __end__
* end
* __HeapLimit
* __StackLimit
* __StackTop
* __stack
* __Vectors_End
* __Vectors_Size
* Used for stack setup:
* __StackLimit
* __StackTop
* Used for heap setup:
* __HeapBase
* __HeapLimit
*/
ENTRY(Reset_Handler)
SECTIONS
{
.text :
{
SECTIONS {
.text : {
KEEP(*(.vectors))
__Vectors_End = .;
__Vectors_Size = __Vectors_End - __Vectors;
__end__ = .;
. = . + 16;
/* KEEP(*(.init)) */
/* KEEP(*(.text._entry)) */
*(.text*)
KEEP(*(.init))
KEEP(*(.fini))
/* .ctors */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* .dtors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.rodata*)
KEEP(*(.eh_frame*))
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
. = ALIGN(4);
__etext = .;
.data : AT (__etext)
{
.data : AT (__etext) {
. = ALIGN(4);
__data_start__ = .;
*(vtable)
*(.data*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
KEEP(*(.jcr*))
. = ALIGN(4);
/* All data end */
__data_end__ = .;
} > RAM
.bss :
{
.bss : {
. = ALIGN(4);
__bss_start__ = .;
*(.bss*)
@ -137,11 +54,8 @@ SECTIONS
__bss_end__ = .;
} > RAM
.heap (COPY):
{
.heap (COPY): {
__HeapBase = .;
__end__ = .;
end = __end__;
KEEP(*(.heap*))
__HeapLimit = .;
} > RAM
@ -149,8 +63,7 @@ SECTIONS
/* .stack_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later */
.stack_dummy (COPY):
{
.stack_dummy (COPY): {
KEEP(*(.stack*))
} > RAM
@ -158,7 +71,6 @@ SECTIONS
* size of stack_dummy section */
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")

View File

@ -89,33 +89,33 @@ __Vectors:
.size __Vectors, . - __Vectors
.text
// I really want to move this into its own section but GDB messes up mapping when I do
@ .section .init
.section .text
.thumb
.thumb_func
.align 2
.global Reset_Handler
.type Reset_Handler, % function
.type Reset_Handler, %function
Reset_Handler:
// Unlock Register
ldr r0, =0x50000100 // REGCTL
movs r1, #0x59
str r1, [r0]
movs r1, #0x16
str r1, [r0]
movs r1, #0x88
str r1, [r0]
ldr r0, =0x50000100 // REGCTL
movs r1, #0x59
str r1, [r0]
movs r1, #0x16
str r1, [r0]
movs r1, #0x88
str r1, [r0]
// Init POR
ldr r2, =0x50000024 // PORCTL
movs r1, #0x5A
ldr r2, =0x50000024 // PORCTL
movs r1, #0x5A
lsls r1,r1,8
adds r1,r1,#0xA5
str r1, [r2]
str r1, [r2]
// Lock registers
movs r1, #0
str r1, [r0]
movs r1, #0
str r1, [r0]
/* Single section scheme.
*
@ -160,10 +160,12 @@ Reset_Handler:
bgt .L_loop3
.L_loop3_done:
#ifndef __ENTRY
#define __ENTRY _entry
#endif
bl __ENTRY
// GCC doesn't realise that we're in a very strict thumb-16 mode here.
// GCC's linker isn't very good at locality.
// These two together mean that we can't perform a simple branch,
// because it'll end up generating a 32-bit instruction.
ldr r0, =_entry
bx r0
.pool
.size Reset_Handler, . - Reset_Handler

View File

@ -39,31 +39,45 @@ If `COM1` is already in use, check what device it is assigned to in Device Manag
Pre-chusan Chunithm uses IO3. This firmware does not (and unfortunately cannot) support IO3. It is recommended to enable the HID keyboard mode, and continue to use keyboard input for IRs.
## Configuration
Hold FN2 for configuration. It is not the same as stock DAO.
## Controls
### General
Tap FN1 to insert a coin. FN2 is currently unbound.
- Pad 1/2 (cell 0): Left wing colour
### Configuration
Hold FN1 for configuration:
- Pad 1/2 (cell 0): Left tower colour
- Pad 3/4 (cell 1): Ground colour
- Pad 5/6 (cell 2): Ground colour when pressed, and separator colour
- Pad 7/8 (cell 3): Right wing colour
- Pad 7/8 (cell 3): Right tower colour
- Pad 9/10 (cell 4): No function
- Pad 11/12 (cell 5): Toggle rainbow effect on/off
- Pad 13/14 (cell 6): Increase/decrease ground brightness
- Pad 15/16 (cell 7): Increase/decrease wing brightness
- Pad 15/16 (cell 7): Increase/decrease tower brightness
- Pad 17/18 (cell 8): No function
- Pad 19/20 (cell 9): No function
- Pad 21/22 (cell 10): No function
- Pad 19/20 (cell 9): System volume up/down
- Pad 21/22 (cell 10): Holds Enter for 5 seconds (insert Aime card)
- Pad 23/24 (cell 11): No function
- Pad 25/26 (cell 12): Increase/decrease sensitivity
- Pad 27/28 (cell 13): No function
- Pad 29/30 (cell 14): Toggle HID keyboard mode
- Pad 31/32 (cell 15): Toggle IO4 emulation mode
- Pad 29/30 (cell 14): No function
- Pad 31/32 (cell 15): Toggle HID keyboard mode
## Calibration
Make sure no hands or objects are near the slider before starting calibration.
### Test menu
To enter the system test menu, double-tap FN2. This will mirror the on-screen controls, and additionally adds controls for the TEST and SERVICE buttons on a real cabinet. Double-tap FN2 again to leave this mode on the controller (note that this is not synced with the game exiting the menu!).
Hold FN1 for two seconds; the slider will flash red for a few seconds, then fill up with a blue bar.
Cell 6 and 7 are the TEST button, and cell 8 and 9 are the SERVICE button.
The slider will then briefly flash green. After this, begin to rub your hands across the slider as much as possible! The cells will turn increasingly green; the greener you can get them the better. Once the dividers have turned green you can save your calibration by pressing FN2.
To access the TEST and SERVICE buttons without entering the test menu, hold FN2 instead.
**Note:** Calibration is a separate process to adjusting the sensor sensitivity. It is recommended to re-calibrate after adjustment of sensitivity. For the best performance, run a re-calibration before every play session to account for changes in your room's temperature and humidity.
## Keyboard Mapping
When the HID keyboard is enabled, the following mapping (UMIGURI defaults) is used:
```
I8U7Y6T5R4E3W2Q1
9KMJNHBGVFCDXSZA
```
with the airs mapped as `0OLP,.`.
Please note that this is slightly different to stock firmware!

11
docs/Colours.md Normal file
View File

@ -0,0 +1,11 @@
| | 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)

79
docs/Development.md Normal file
View File

@ -0,0 +1,79 @@
# Development
## Requirements
- Windows (Strongly recommend. Compiling on anything else is not officially supported.)
- [Make for Windows](https://gnuwin32.sourceforge.net/packages/make.htm)
- On the path, please
- [Arm GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) (Currently using 13.2 Rel1)
- To make your life easier, ensure this is located at `C:\Program Files (x86)\GNU Arm Embedded Toolchain\13.2 Rel1`
- [picolibc 1.8.6](https://github.com/picolibc/picolibc/releases/download/1.8.6/picolibc-1.8.6-13.2.Rel1.zip)
- Place the contents of this zip file in `picolibc`
VSCode is recommended for development, and all recommended extensions should be installed.
## Compiling and Uploading
Run `make -j` to compile the firmware.
For rapid development, the HID bootloader is quite annoying. `flash.cmd` is provided which reprograms the chip using an ST-Link V2. This requires an OpenOCD installation in the root project folder, along with an existing compiled host bootloader binary.
For debugging in VSCode, install the recommended extensions then hit F5. This requires an OpenOCD installation in the root project folder, and an ST-Link V2.
## NUC123 BSP
The `NUC123` folder is a heavily reduced form of the complete BSP provided by Nuvoton. Modifications should not be made to any files within the `inc` or `StdDriver` folder.
`startup_NUC123.s` is the program entrypoint, and may require modification.
`NUC123.ld` is a modified version of the GCC linker script provided by Nuvoton, and may require modification.
To pull in additional BSP drivers, if required, the `Makefile` should be modified.
## CONFIG Flags
Stock TASOLLERs ship with Config0=FFFFFF7D, Config1=FFFFFFFF. The breakdown of Config0 is as follows:
| Bit | Function | Stock Value | Description |
| ----- | ---------- | ----------- | ----------------------------------- |
| 31 | `CWDTEN` | 1 | Watchdog inactive |
| 30 | `CWDTPDEN` | 1 | N/A |
| 29:28 | Reserved | 11 | |
| 27 | `CGPFMFP` | 1 | PF.0/PF.1 used for external crystal |
| 26:24 | `CFOSC` | 111 | 22.11284 MHz internal HIRC |
| 23 | `CBODEN` | 1 | Brown-out detection disabled |
| 22:21 | `CBOV` | 11 | Brown-out voltage is 4.5V |
| 20 | `CBORST` | 1 | Brown-out reset disabled |
| 19:11 | Reserved | 111111111 | |
| 10 | `CIOINI` | 1 | GPIO defaults to quasi-directional |
| 9:8 | Reserved | 11 | |
| 7:6 | `CBS` | 01 | Boot from LDROM without IAP |
| 5:3 | Reserved | 111 | |
| 2 | `DFVSEN` | 1 | Data flash fixed at 4kB |
| 1 | `LOCK` | 0 | Flash memory is locked |
| 0 | `DFEN` | 1 | N/A |
For debugging, `LOCK` being enabled can be problematic. Unlocking the processor involves performing a complete chip erase, at which point we must provision out bootloader. We can then set config0 to `FFFFFF7F`, allowing access to flash memory for debugging. `flash.cmd` does this.
Config1 is just used for `DFBADR` and because `DFVSEN=1` this is ignored anyway.
Of note, neither the watchdog nor brown-out detection is enabled. This custom firmware makes no attempt to alter this, as it would present complications when reverting to stock firmware. I would much prefer to have the watchdog enabled, trust me.
## USB
This firmware is compliant with the USB specification. The following USB3CV tests are used to verify compliance:
- `Chapter 9 Tests [USB 2 devices]`
- `Connector Type Tests`
- `HID Tests`
If you make changes to the USB code, it is recommended to re-run these tests to ensure continued compliance.
Additionally, the following tests may be run for diagnostics:
- `Device Summary`
- `Current Measurement Test [USB 2 devices]`
xHSETT can be used to directly control the USB device, such as sending SUSPEND signals.
## Airs
See https://www.shinkoh-elecs.jp/wp-content/uploads/2024/05/C_KB1281_1581_24A.pdf
The loop of wire between the two wings is for connecting emitter pin 3 to detector pin 1.
The right bottom is an emitter, and then it alternates up from there.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,349 @@
<HTML><HEAD>
<META http-equiv="X-UA-Compatible" content="IE=edge" />
<META charset="UTF-8" />
<STYLE type='text/css'>
BODY { background-color: #DFDFDF; }
.dm_display
{
color: Navy;
font-family: monospace;
background-color: White;
border-width: 3px;
border-style: outset;
}
#suite-output
{
background-color: #FFFFDD;
border: ridge 3px #FFFFDD;
border-collapse: collapse;
padding: 0;
margin: 0;
empty-cells: hide
}
#test-header
{
font-weight: bold;
font-size: large;
border-top: ridge 2px #FFFFDD;
border-bottom: ridge 2px #FFFFDD;
padding: 0;
margin: 0;
}
.test-pass, .test-fail
{
float: right;
border-left: ridge 2px #FFFDD;
min-width: 6em;
}
.test-pass { background-color: #00FF00; }
.test-fail { background-color: #FF8888; }
.test-details
{
clear: both;
font-family: monospace;
padding: 0;
margin: 0;
empty-cells: hide;
}
.label
{
float: left;
width: 6em;
}
.content
{
white-space: pre-wrap;
margin-left: 7em;
}
.err_log, .warn_log
{
color: Red;
}
.err_log { font-weight: bold; }
.unexpected_log
{
color: Red;
background-color: Yellow;
font-weight: bold;
font-style: italic;
}
</STYLE>
<TITLE>Connector Type Tests - 2024-07-23 19-45-53</TITLE>
<META name='Suite-Name' content='Connector Type Tests.cvtests' />
<META name='Test-Version' content='GuiCV.exe ver 4.1.0.0' />
<META name='Test-Version' content='BaseUtilities.dll ver 4.1.0.0' />
<META name='Test-Version' content='GuiHelper.dll ver 4.1.0.0' />
<META name='Test-Version' content='ComplianceUtilities.dll ver 4.1.0.0' />
<META name='Test-Version' content='TestSuiteParser.dll ver 4.1.0.0' />
<META name='Test-Version' content='xhci_DevIOCTL.dll ver 2.2.7.0' />
<META name='Test-Version' content='xhci_TestServices.dll ver 2.2.7.0' />
<META name='Test-Version' content='USBUtilities.dll ver 1.4.5.1' />
<META name='Test-Version' content='StackSwitcher.dll ver 1.4.5.1' />
<META name='Test-Version' content='xhci_CVServices.dll ver 2.2.7.0' />
<META name='Test-Version' content='xhci_ConnectorType.dll ver 3.1.3.0' />
<META name='Host-Controller' content='VID=1022,PID=149C' />
<META name='Device-Under-Test' content='VID=0CA3, PID=0021' />
<META name='Device-Speed' content='Full Speed' />
<META name='Topology' content='XHCI HC -- DUT' />
<META name='Test-Pass' content='TD 9.1 PD Configuration Descriptor Test (Configuration Index 0x00) - Device State Configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='TD 9.2 PD Capability Descriptor Test (Configuration Index 0x00) - Device State Configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='TD 9.3 PD Battery Info Capability Descriptor Test (Configuration Index 0x00) - Device State Configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='TD 9.4 PD Consumer Port Capability Descriptor Test (Configuration Index 0x00) - Device State Configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='TD 9.5 PD Provider Port Capability Descriptor Test (Configuration Index 0x00) - Device State Configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='TD 9.6 PD Battery Status Test (Configuration Index 0x00) - Device State Configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='TD 9.7 PD Remote Wake Test - Enabled (Configuration Index 0x00) - Device State Configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='TD 9.7 Remote Wake Test - Disabled (Configuration Index 0x00) - Device State Configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='TD 9.8 PD CHARGING_POLICY Test (Configuration Index 0x00) - Device State Configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='TD 9.9 PD Hub Bridge Test on single host (Configuration Index 0x00) - Device State Configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Suite-Result' content='PASS' />
<META name='Log-Name' content='Connector Type Tests - 2024-07-23 19-45-53.html' />
<META name='Test-Count' content='10' />
</HEAD>
<BODY><DIV id='suite-summary'>
<DIV><STRONG>APPLICATION</STRONG>: USB 3 Gen X Command Verifier<BR></DIV>
<DIV><STRONG>TEST SUITE</STRONG>: Connector Type Tests.cvtests<BR></DIV>
<DIV><STRONG>OPERATING SYSTEM</STRONG>: <BR></DIV>
<DIV><STRONG>WORKSTATION</STRONG>: <BR></DIV>
<DIV><STRONG>DATE</STRONG>: Tuesday, July 23, 2024<BR></DIV>
<DIV><STRONG>TIME</STRONG>: 07:46:09 PM<BR></DIV>
<DIV><STRONG>OPERATOR</STRONG>: Emma<BR></DIV>
<DIV><STRONG>NUMBER OF TESTS</STRONG>: 10<BR></DIV>
<DIV><STRONG>LOG NAME</STRONG>: Connector Type Tests - 2024-07-23 19-45-53<BR></DIV>
<DIV><STRONG>RESULT</STRONG>: passed<BR></DIV>
<HR>
</DIV>
<BR><BR>
<DIV id='suite-output'>
<DIV id='test-header'>InitializeTestSuite</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Test log initialized.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Log Level: Normal</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>User Input module initialized</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Windows Version 22H2 (OS Build 19045)</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>GuiCV.exe ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>BaseUtilities.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>GuiHelper.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>ComplianceUtilities.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>TestSuiteParser.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_DevIOCTL.dll ver 2.2.7.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_TestServices.dll ver 2.2.7.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>USBUtilities.dll ver 1.4.5.1</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>StackSwitcher.dll ver 1.4.5.1</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_CVServices.dll ver 2.2.7.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_ConnectorType.dll ver 3.1.3.0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>XHCISpecVersion: 1.10.</DIV></DIV>
<DIV style='font-weight: 700;color:#00007F'><DIV class='label'>INFO</DIV><DIV class='content'>Host 1 selected: xHCI Host: VID=0x1022, PID=0x149C (PCI bus 45, device 0, function 3)</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>User selection from list: &quot;FS Device (HID) addr=1: VID=0CA3, PID=0021 RouteString 0x00000&quot;</DIV></DIV>
<DIV style='font-weight: 700;color:#00007F'><DIV class='label'>INFO</DIV><DIV class='content'>Please select USB Device to test</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>USB Device Under Test is operating at Full Speed.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Topology: XHCI HC -- DUT</DIV></DIV>
</DIV>
<DIV id='test-header'>Get Number Of Configurations</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>USB Version number of device: 2.00</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Number of configurations: 1.</DIV></DIV>
</DIV>
<DIV id='test-header'>Get Number Of Other Speed Configurations</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Number of Other Speed configurations: 0.</DIV></DIV>
</DIV>
<DIV id='test-header'>Get Number Of Ports</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test is not a Hub.</DIV></DIV>
</DIV>
<DIV id='test-header'>Find Out Which Downstream Facing Ports Are Physically Inaccessible</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test is not a Hub.</DIV></DIV>
</DIV>
<DIV id='test-header'>Get Active Power State</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Acquiring power state helper.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>User selected power state: &quot;Standard USB Current&quot;.</DIV></DIV>
</DIV>
<DIV id='test-header'>Read VIF from file</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test is embedded, and cannot have a Vendor Info File (VIF) for its upstream facing port.</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_8'>TD 9.1 PD Configuration Descriptor Test (Configuration Index 0x00) - Device State Configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:45:58</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Configuration Index 0x00 has a Configuration Value of 1.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Initialized the device to the Configured state with Configuration Value 1.
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF contains 0 components.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF has 0 Upstream Facing Ports.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test does not have a BOS descriptor and so does not support PD.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test does not support PD.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:45:59</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ TD 9.1 PD Configuration Descriptor Test (Configuration Index 0x00) - Device State Configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_9'>TD 9.2 PD Capability Descriptor Test (Configuration Index 0x00) - Device State Configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:45:59</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Configuration Index 0x00 has a Configuration Value of 1.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Initialized the device to the Configured state with Configuration Value 1.
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF contains 0 components.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF has 0 Upstream Facing Ports.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test does not have a BOS descriptor and so does not support PD.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:00</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ TD 9.2 PD Capability Descriptor Test (Configuration Index 0x00) - Device State Configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_10'>TD 9.3 PD Battery Info Capability Descriptor Test (Configuration Index 0x00) - Device State Configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:01</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Configuration Index 0x00 has a Configuration Value of 1.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Initialized the device to the Configured state with Configuration Value 1.
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF contains 0 components.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF has 0 Upstream Facing Ports.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test does not have a BOS descriptor and so does not support PD.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:02</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ TD 9.3 PD Battery Info Capability Descriptor Test (Configuration Index 0x00) - Device State Configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_11'>TD 9.4 PD Consumer Port Capability Descriptor Test (Configuration Index 0x00) - Device State Configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:02</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Configuration Index 0x00 has a Configuration Value of 1.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Initialized the device to the Configured state with Configuration Value 1.
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF contains 0 components.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF has 0 Upstream Facing Ports.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test does not have a BOS descriptor and so does not support PD.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:03</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ TD 9.4 PD Consumer Port Capability Descriptor Test (Configuration Index 0x00) - Device State Configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_12'>TD 9.5 PD Provider Port Capability Descriptor Test (Configuration Index 0x00) - Device State Configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:03</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Configuration Index 0x00 has a Configuration Value of 1.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Initialized the device to the Configured state with Configuration Value 1.
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF contains 0 components.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF has 0 Upstream Facing Ports.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test does not have a BOS descriptor and so does not support PD.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:04</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ TD 9.5 PD Provider Port Capability Descriptor Test (Configuration Index 0x00) - Device State Configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_13'>TD 9.6 PD Battery Status Test (Configuration Index 0x00) - Device State Configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:04</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Configuration Index 0x00 has a Configuration Value of 1.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Initialized the device to the Configured state with Configuration Value 1.
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF contains 0 components.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF has 0 Upstream Facing Ports.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test does not have a BOS descriptor and so does not support PD.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:05</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ TD 9.6 PD Battery Status Test (Configuration Index 0x00) - Device State Configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_14'>TD 9.7 PD Remote Wake Test - Enabled (Configuration Index 0x00) - Device State Configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:05</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Configuration Index 0x00 has a Configuration Value of 1.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Initialized the device to the Configured state with Configuration Value 1.
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF contains 0 components.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF has 0 Upstream Facing Ports.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test does not have a BOS descriptor and so does not support PD.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:06</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ TD 9.7 PD Remote Wake Test - Enabled (Configuration Index 0x00) - Device State Configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_15'>TD 9.7 Remote Wake Test - Disabled (Configuration Index 0x00) - Device State Configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:06</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Configuration Index 0x00 has a Configuration Value of 1.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Initialized the device to the Configured state with Configuration Value 1.
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF contains 0 components.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF has 0 Upstream Facing Ports.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test does not have a BOS descriptor and so does not support PD.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:07</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ TD 9.7 Remote Wake Test - Disabled (Configuration Index 0x00) - Device State Configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_16'>TD 9.8 PD CHARGING_POLICY Test (Configuration Index 0x00) - Device State Configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:07</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Configuration Index 0x00 has a Configuration Value of 1.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Initialized the device to the Configured state with Configuration Value 1.
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF contains 0 components.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>VIF has 0 Upstream Facing Ports.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test does not have a BOS descriptor and so does not support PD.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:08</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ TD 9.8 PD CHARGING_POLICY Test (Configuration Index 0x00) - Device State Configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_17'>TD 9.9 PD Hub Bridge Test on single host (Configuration Index 0x00) - Device State Configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:08</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Configuration Index 0x00 has a Configuration Value of 1.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Initialized the device to the Configured state with Configuration Value 1.
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device Under Test is not a hub.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:09</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ TD 9.9 PD Hub Bridge Test on single host (Configuration Index 0x00) - Device State Configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'>PDCommon_CleanupVIFs</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Cleaning up VIF for Upstream Facing Port</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Cleaning up VIF for Downstream Facing Ports.</DIV></DIV>
</DIV>
<DIV id='test-header'>Cleanup</DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>TEST SUITE SUMMARY:
[ Fails (0); Aborts (0); Warnings (0) ]</DIV></DIV>
<DIV style='font-weight: 700;font-style:italic;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>TEST RESULTS:
[ Passed (10); Failed (0) ]</DIV></DIV>
</DIV>
</DIV>
</BODY></HTML>

View File

@ -0,0 +1,205 @@
<HTML><HEAD>
<META http-equiv="X-UA-Compatible" content="IE=edge" />
<META charset="UTF-8" />
<STYLE type='text/css'>
BODY { background-color: #DFDFDF; }
.dm_display
{
color: Navy;
font-family: monospace;
background-color: White;
border-width: 3px;
border-style: outset;
}
#suite-output
{
background-color: #FFFFDD;
border: ridge 3px #FFFFDD;
border-collapse: collapse;
padding: 0;
margin: 0;
empty-cells: hide
}
#test-header
{
font-weight: bold;
font-size: large;
border-top: ridge 2px #FFFFDD;
border-bottom: ridge 2px #FFFFDD;
padding: 0;
margin: 0;
}
.test-pass, .test-fail
{
float: right;
border-left: ridge 2px #FFFDD;
min-width: 6em;
}
.test-pass { background-color: #00FF00; }
.test-fail { background-color: #FF8888; }
.test-details
{
clear: both;
font-family: monospace;
padding: 0;
margin: 0;
empty-cells: hide;
}
.label
{
float: left;
width: 6em;
}
.content
{
white-space: pre-wrap;
margin-left: 7em;
}
.err_log, .warn_log
{
color: Red;
}
.err_log { font-weight: bold; }
.unexpected_log
{
color: Red;
background-color: Yellow;
font-weight: bold;
font-style: italic;
}
</STYLE>
<TITLE>Device Summary - 2024-07-23 19-46-25</TITLE>
<META name='Suite-Name' content='Device Summary.cvtests' />
<META name='Test-Version' content='GuiCV.exe ver 4.1.0.0' />
<META name='Test-Version' content='BaseUtilities.dll ver 4.1.0.0' />
<META name='Test-Version' content='GuiHelper.dll ver 4.1.0.0' />
<META name='Test-Version' content='ComplianceUtilities.dll ver 4.1.0.0' />
<META name='Test-Version' content='TestSuiteParser.dll ver 4.1.0.0' />
<META name='Test-Version' content='xhci_DevIOCTL.dll ver 2.2.7.0' />
<META name='Test-Version' content='xhci_TestServices.dll ver 2.2.7.0' />
<META name='Test-Version' content='USBUtilities.dll ver 1.4.5.1' />
<META name='Test-Version' content='StackSwitcher.dll ver 1.4.5.1' />
<META name='Test-Version' content='xhci_CVServices.dll ver 2.2.7.0' />
<META name='Host-Controller' content='VID=1022,PID=149C' />
<META name='Device-Under-Test' content='VID=0CA3, PID=0021' />
<META name='Device-Speed' content='Full Speed' />
<META name='Topology' content='XHCI HC -- DUT' />
<META name='Test-Pass' content='Device Summary [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Suite-Result' content='PASS' />
<META name='Log-Name' content='Device Summary - 2024-07-23 19-46-25.html' />
<META name='Test-Count' content='1' />
</HEAD>
<BODY><DIV id='suite-summary'>
<DIV><STRONG>APPLICATION</STRONG>: USB 3 Gen X Command Verifier<BR></DIV>
<DIV><STRONG>TEST SUITE</STRONG>: Device Summary.cvtests<BR></DIV>
<DIV><STRONG>OPERATING SYSTEM</STRONG>: <BR></DIV>
<DIV><STRONG>WORKSTATION</STRONG>: <BR></DIV>
<DIV><STRONG>DATE</STRONG>: Tuesday, July 23, 2024<BR></DIV>
<DIV><STRONG>TIME</STRONG>: 07:46:29 PM<BR></DIV>
<DIV><STRONG>OPERATOR</STRONG>: Emma<BR></DIV>
<DIV><STRONG>NUMBER OF TESTS</STRONG>: 1<BR></DIV>
<DIV><STRONG>LOG NAME</STRONG>: Device Summary - 2024-07-23 19-46-25<BR></DIV>
<DIV><STRONG>RESULT</STRONG>: passed<BR></DIV>
<HR>
</DIV>
<BR><BR>
<DIV id='suite-output'>
<DIV id='test-header'>Initialize Test Suite</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Test log initialized.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Log Level: Normal</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>User Input module initialized</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Windows Version 22H2 (OS Build 19045)</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>GuiCV.exe ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>BaseUtilities.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>GuiHelper.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>ComplianceUtilities.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>TestSuiteParser.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_DevIOCTL.dll ver 2.2.7.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_TestServices.dll ver 2.2.7.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>USBUtilities.dll ver 1.4.5.1</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>StackSwitcher.dll ver 1.4.5.1</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_CVServices.dll ver 2.2.7.0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>XHCISpecVersion: 1.10.</DIV></DIV>
<DIV style='font-weight: 700;color:#00007F'><DIV class='label'>INFO</DIV><DIV class='content'>Host 1 selected: xHCI Host: VID=0x1022, PID=0x149C (PCI bus 45, device 0, function 3)</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>User selection from list: &quot;FS Device (HID) addr=1: VID=0CA3, PID=0021 RouteString 0x00000&quot;</DIV></DIV>
<DIV style='font-weight: 700;color:#00007F'><DIV class='label'>INFO</DIV><DIV class='content'>Please select USB Device to test</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>USB Device Under Test is operating at Full Speed.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Topology: XHCI HC -- DUT</DIV></DIV>
</DIV>
<DIV id='test-header'>Get Number Of Configurations</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>USB Version number of device: 2.00</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Number of configurations: 1.</DIV></DIV>
</DIV>
<DIV id='test-header'>Get Number Of Other Speed Configurations</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Number of Other Speed configurations: 0.</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_4'>Device Summary</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:28</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Host VEN = 1022h
Host DEV = 149ch
Host Revision number = 0h
Host HCI Version = 110
</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Device is attached to port 4</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> USB Version: 2.00</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Looking for Vendor Name File: usbif.json</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Device Vendor : 3235 (0xca3) -- SEGA CORPORATION</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Device Product ID : 33 (0x21)</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Device Revision (bcdDevice) : 0x0100</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Checking iProduct String Descriptor: index = 0x02.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>String Descriptor : &quot;TASOLLER&quot;. (ENGLISH_US)</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>String Descriptor : &quot;TASOLLER&quot;. (NEUTRAL)</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> bDeviceClass = 0x0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> bDeviceProtocol = 0x0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> bDeviceSubClass = 0x0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Device Type : HID</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Device Speed : Full Speed</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Number of configurations device supports : 1</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Got configuration descriptor for config index : 0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Full length of configuration descriptor is 132 bytes</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Max Power : 500mA</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Self-powered : No</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Remote Wakeup : No</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Number of interfaces device supports : 4</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Descriptor for interface 0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 2</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Interface class code indicates [CDC-Control] Interface</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Descriptor for interface 1</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : a</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Interface class code indicates [CDC-Data] Interface</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Descriptor for interface 2</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 3</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Interface class code indicates [HID] Interface</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Descriptor for interface 3</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 3</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'> Interface class code indicates [HID] Interface</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:29</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ Device Summary:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'>Summary</DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>TEST SUITE SUMMARY:
[ Fails (0); Aborts (0); Warnings (0) ]</DIV></DIV>
<DIV style='font-weight: 700;font-style:italic;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>TEST RESULTS:
[ Passed (1); Failed (0) ]</DIV></DIV>
</DIV>
</DIV>
</BODY></HTML>

View File

@ -0,0 +1,254 @@
<HTML><HEAD>
<META http-equiv="X-UA-Compatible" content="IE=edge" />
<META charset="UTF-8" />
<STYLE type='text/css'>
BODY { background-color: #DFDFDF; }
.dm_display
{
color: Navy;
font-family: monospace;
background-color: White;
border-width: 3px;
border-style: outset;
}
#suite-output
{
background-color: #FFFFDD;
border: ridge 3px #FFFFDD;
border-collapse: collapse;
padding: 0;
margin: 0;
empty-cells: hide
}
#test-header
{
font-weight: bold;
font-size: large;
border-top: ridge 2px #FFFFDD;
border-bottom: ridge 2px #FFFFDD;
padding: 0;
margin: 0;
}
.test-pass, .test-fail
{
float: right;
border-left: ridge 2px #FFFDD;
min-width: 6em;
}
.test-pass { background-color: #00FF00; }
.test-fail { background-color: #FF8888; }
.test-details
{
clear: both;
font-family: monospace;
padding: 0;
margin: 0;
empty-cells: hide;
}
.label
{
float: left;
width: 6em;
}
.content
{
white-space: pre-wrap;
margin-left: 7em;
}
.err_log, .warn_log
{
color: Red;
}
.err_log { font-weight: bold; }
.unexpected_log
{
color: Red;
background-color: Yellow;
font-weight: bold;
font-style: italic;
}
</STYLE>
<TITLE>HID Tests - 2024-07-23 19-46-13</TITLE>
<META name='Suite-Name' content='HID Tests.cvtests' />
<META name='Test-Version' content='GuiCV.exe ver 4.1.0.0' />
<META name='Test-Version' content='BaseUtilities.dll ver 4.1.0.0' />
<META name='Test-Version' content='GuiHelper.dll ver 4.1.0.0' />
<META name='Test-Version' content='ComplianceUtilities.dll ver 4.1.0.0' />
<META name='Test-Version' content='TestSuiteParser.dll ver 4.1.0.0' />
<META name='Test-Version' content='xhci_DevIOCTL.dll ver 2.2.7.0' />
<META name='Test-Version' content='xhci_TestServices.dll ver 2.2.7.0' />
<META name='Test-Version' content='USBUtilities.dll ver 1.4.5.1' />
<META name='Test-Version' content='StackSwitcher.dll ver 1.4.5.1' />
<META name='Test-Version' content='xhci_CVServices.dll ver 2.2.7.0' />
<META name='Test-Version' content='HidParser.dll ver 1.4.5.0' />
<META name='Test-Version' content='xhci_HID.dll ver 3.1.3.0' />
<META name='Host-Controller' content='VID=1022,PID=149C' />
<META name='Device-Under-Test' content='VID=0CA3, PID=0021' />
<META name='Device-Speed' content='Full Speed' />
<META name='Topology' content='XHCI HC -- DUT' />
<META name='Test-Pass' content='HID Class WhichSpecCompliant Test [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='HID Class Descriptor Test (Configuration Index 0) - configured [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='HID Class Protocol Test (Configuration Index 0) [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='HID Class GET/SET Idle Test (Configuration Index 0) [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Test-Pass' content='HID Class Report Descriptor Test (Configuration Index 0) [Fails (0); Aborts (0); Warnings (0)]' />
<META name='Suite-Result' content='PASS' />
<META name='Log-Name' content='HID Tests - 2024-07-23 19-46-13.html' />
<META name='Test-Count' content='5' />
</HEAD>
<BODY><DIV id='suite-summary'>
<DIV><STRONG>APPLICATION</STRONG>: USB 3 Gen X Command Verifier<BR></DIV>
<DIV><STRONG>TEST SUITE</STRONG>: HID Tests.cvtests<BR></DIV>
<DIV><STRONG>OPERATING SYSTEM</STRONG>: <BR></DIV>
<DIV><STRONG>WORKSTATION</STRONG>: <BR></DIV>
<DIV><STRONG>DATE</STRONG>: Tuesday, July 23, 2024<BR></DIV>
<DIV><STRONG>TIME</STRONG>: 07:46:21 PM<BR></DIV>
<DIV><STRONG>OPERATOR</STRONG>: Emma<BR></DIV>
<DIV><STRONG>NUMBER OF TESTS</STRONG>: 5<BR></DIV>
<DIV><STRONG>LOG NAME</STRONG>: HID Tests - 2024-07-23 19-46-13<BR></DIV>
<DIV><STRONG>RESULT</STRONG>: passed<BR></DIV>
<HR>
</DIV>
<BR><BR>
<DIV id='suite-output'>
<DIV id='test-header'>Initialize Test Suite</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Test log initialized.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Log Level: Normal</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>User Input module initialized</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Windows Version 22H2 (OS Build 19045)</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>GuiCV.exe ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>BaseUtilities.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>GuiHelper.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>ComplianceUtilities.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>TestSuiteParser.dll ver 4.1.0.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_DevIOCTL.dll ver 2.2.7.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_TestServices.dll ver 2.2.7.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>USBUtilities.dll ver 1.4.5.1</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>StackSwitcher.dll ver 1.4.5.1</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_CVServices.dll ver 2.2.7.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>HidParser.dll ver 1.4.5.0</DIV></DIV>
<DIV style='color:#00005F'><DIV class='label'>INFO</DIV><DIV class='content'>xhci_HID.dll ver 3.1.3.0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>XHCISpecVersion: 1.10.</DIV></DIV>
<DIV style='font-weight: 700;color:#00007F'><DIV class='label'>INFO</DIV><DIV class='content'>Host 1 selected: xHCI Host: VID=0x1022, PID=0x149C (PCI bus 45, device 0, function 3)</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>User selection from list: &quot;FS Device (HID) addr=1: VID=0CA3, PID=0021 RouteString 0x00000&quot;</DIV></DIV>
<DIV style='font-weight: 700;color:#00007F'><DIV class='label'>INFO</DIV><DIV class='content'>Please select HID Device to test</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>HID Device Under Test is operating at Full Speed.</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Topology: XHCI HC -- DUT</DIV></DIV>
</DIV>
<DIV id='test-header'>Get Number Of Configurations</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>USB Version number of device: 2.00</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Number of configurations: 1.</DIV></DIV>
</DIV>
<DIV id='test-header'>Get Number Of Configurations - Other Speed</DIV>
<DIV class='test-details'>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Number of Other Speed configurations: 0.</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_4'>HID Class WhichSpecCompliant Test</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:16</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>USB Version number of device: 2.00</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Interface 0x2 Alternate Setting 0x0 is draft 4 Compliant</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Interface 0x3 Alternate Setting 0x0 is draft 4 Compliant</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:17</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ HID Class WhichSpecCompliant Test:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_5'>HID Class Descriptor Test (Configuration Index 0) - configured</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:17</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Testing HID descriptor for Interface 0x2</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>HID descriptor total length : 0x9</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>HID descriptor type : 0x21</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>HID specification version : 0x110</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>CountryCode : 0x0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Number of other descriptors present : 0x1</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>HID report descriptor length : 0x71</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Testing HID descriptor for Interface 0x3</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>HID descriptor total length : 0x9</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>HID descriptor type : 0x21</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>HID specification version : 0x110</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>CountryCode : 0x0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Number of other descriptors present : 0x1</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>HID report descriptor length : 0x97</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:18</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ HID Class Descriptor Test (Configuration Index 0) - configured:
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_6'>HID Class Protocol Test (Configuration Index 0)</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:18</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Starting GET/SET protocol tests for interface : 2</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SET protocol request is optional. Skipping tests</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Starting GET/SET protocol tests for interface : 3</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SET protocol request is optional. Skipping tests</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:19</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ HID Class Protocol Test (Configuration Index 0):
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_7'>HID Class GET/SET Idle Test (Configuration Index 0)</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:19</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x1. Setting idle rate to : 0x7F</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x1. Setting idle rate to : 0xFF</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x1. Setting idle rate to : 0x0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x2. Setting idle rate to : 0x7F</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x2. Setting idle rate to : 0xFF</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x2. Setting idle rate to : 0x0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x3. Setting idle rate to : 0x7F</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x3. Setting idle rate to : 0xFF</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x3. Setting idle rate to : 0x0</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x4. Setting idle rate to : 0x7F</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x4. Setting idle rate to : 0xFF</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>GET/SETIdle test for report ID 0x4. Setting idle rate to : 0x0</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:20</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ HID Class GET/SET Idle Test (Configuration Index 0):
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'><A name='anchor_8'>HID Class Report Descriptor Test (Configuration Index 0)</A><DIV class='test-pass'>Passed</DIV></DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#0000AA'><DIV class='label'>INFO</DIV><DIV class='content'>Start time: Jul 23, 2024 - 19:46:20</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Report descriptor length: 113</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Item count in report descriptor: 54</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Report descriptor test passed. Proceeding to next descriptor</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Report descriptor length: 151</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Item count in report descriptor: 71</DIV></DIV>
<DIV><DIV class='label'>INFO</DIV><DIV class='content'>Report descriptor test passed. Proceeding to next descriptor</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>
Stop time: Jul 23, 2024 - 19:46:21</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Duration: 1 second.</DIV></DIV>
<DIV style='font-weight: 700;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>Stopping Test [ HID Class Report Descriptor Test (Configuration Index 0):
Number of: Fails (0); Aborts (0); Warnings (0) ]
</DIV></DIV>
</DIV>
<DIV id='test-header'>Summary</DIV>
<DIV class='test-details'>
<DIV style='font-weight: 700;font-style:italic;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>TEST SUITE SUMMARY:
[ Fails (0); Aborts (0); Warnings (0) ]</DIV></DIV>
<DIV style='font-weight: 700;font-style:italic;color:#007F00'><DIV class='label'>INFO</DIV><DIV class='content'>TEST RESULTS:
[ Passed (5); Failed (0) ]</DIV></DIV>
</DIV>
</DIV>
</BODY></HTML>

View File

@ -1,17 +1,31 @@
@..\..\openocd-0.12.0-3\bin\openocd.exe -f interface/stlink-v2.cfg -f ..\..\nucxxx.cfg ^
@ECHO off
FOR /F "tokens=* USEBACKQ" %%g IN (`
..\..\openocd-0.12.0-3\bin\openocd.exe -f ..\..\nucxxx.cfg ^
-c "init; PrintPart; exit" 2^>NUL
`) DO (SET "PART_NUM=%%g")
IF NOT "%PART_NUM%"=="NUC123SD4AN0" (
ECHO Invalid part number %PART_NUM%. Make sure you're connected to HOST not LED!
EXIT /B 1
)
REM SET LDROM_PATH=../bootloader/host_bl.bin
SET LDROM_PATH=../../../TASOLLER_HOST_LDROM.bin
SET APROM_PATH=host_aprom.bin
..\..\openocd-0.12.0-3\bin\openocd.exe -f ..\..\nucxxx.cfg ^
-c "SysReset halt" ^
-c "flash read_bank 1 dataflash.bin" ^
-c "ChipErase" ^
-c "exit"
@..\..\openocd-0.12.0-3\bin\openocd.exe -f interface/stlink-v2.cfg -f ..\..\nucxxx.cfg ^
-c "SysReset halt" ^
-c "WriteConfigRegs 0xFFFFFF7F 0xFFFFFFFF" ^
-c "ReadConfigRegs" ^
-c "program ../bootloader/host_bl.bin 0x100000" ^
-c "program host_aprom.bin 0" ^
-c "program %LDROM_PATH% 0x100000" ^
-c "program %APROM_PATH% 0" ^
-c "program dataflash.bin 0x1F000" ^
-c "ReadConfigRegs" ^
-c "SysReset aprom run" ^
-c "exit"
-c "exit" -d0
@del dataflash.bin
REM Clean up after ourself
del dataflash.bin

10
src/_compiler.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#ifndef __packed
#if defined(__CC_ARM)
#elif defined(__GNUC__)
#define __packed __attribute__((packed))
#else
#error Unknown compiler
#endif
#endif

View File

@ -1,6 +0,0 @@
.syntax unified
.global DelayCycles
DelayCycles:
subs r0, r0, #1
bcs DelayCycles
bx lr

View File

@ -74,7 +74,7 @@ static const uint8_t IO4_ReportDescriptor[] = {
HID_COLLECTION(APPLICATION),
HID_USAGE(UNDEFINED),
HID_LOGICAL_MINIMUM(1, 0),
HID_LOGICAL_MAXIMUM(1, 255),
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,
@ -85,7 +85,7 @@ static const uint8_t IO4_ReportDescriptor[] = {
};
static const uint8_t Keyboard_ReportDescriptor[] = {
// Keyboard input descriptor
// Keyboard input report
HID_USAGE_PAGE(GENERIC_DESKTOP),
HID_USAGE(KEYBOARD),
HID_COLLECTION(APPLICATION),
@ -93,7 +93,7 @@ static const uint8_t Keyboard_ReportDescriptor[] = {
HID_USAGE_PAGE(KEYBOARD),
HID_LOGICAL_MINIMUM(1, 0),
HID_LOGICAL_MAXIMUM(1, 231),
HID_LOGICAL_MAXIMUM(2, 231),
HID_USAGE_MINIMUM(1, 0),
HID_USAGE_MAXIMUM(1, 231),
HID_REPORT_SIZE(8),
@ -102,130 +102,37 @@ static const uint8_t Keyboard_ReportDescriptor[] = {
HID_END_COLLECTION(APPLICATION),
// Debugging reports descriptors (they dump the raw PSoC data)
HID_USAGE_PAGE(GENERIC_DESKTOP),
HID_USAGE(JOYSTICK),
// Consumer control report
HID_USAGE_PAGE(CONSUMER),
HID_USAGE(CONSUMER_CONTROL),
HID_COLLECTION(APPLICATION),
HID_REPORT_ID(HID_REPORT_ID_CONSUMER_CONTROL),
HID_REPORT_ID(HID_REPORT_ID_DEBUG_A),
HID_USAGE(POINTER),
HID_COLLECTION(LOGICAL),
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),
HID_USAGE_PAGE(CONSUMER),
HID_USAGE_MINIMUM(1, 0),
HID_USAGE_MAXIMUM(2, 0x0FFF),
HID_LOGICAL_MINIMUM(1, 0),
HID_LOGICAL_MAXIMUM(4, 0xffff),
HID_PHYSICAL_MINIMUM(1, 0),
HID_PHYSICAL_MAXIMUM(4, 0xffff),
HID_REPORT_COUNT(16),
HID_LOGICAL_MAXIMUM(2, 0x0FFF),
HID_REPORT_SIZE(16),
HID_INPUT(DATA, VARIABLE, ABSOLUTE),
HID_END_COLLECTION(LOGICAL),
HID_REPORT_ID(HID_REPORT_ID_DEBUG_B),
HID_USAGE(POINTER),
HID_COLLECTION(LOGICAL),
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),
HID_LOGICAL_MINIMUM(1, 0),
HID_LOGICAL_MAXIMUM(4, 0xffff),
HID_PHYSICAL_MINIMUM(1, 0),
HID_PHYSICAL_MAXIMUM(4, 0xffff),
HID_REPORT_SIZE(16),
HID_REPORT_COUNT(16),
HID_INPUT(DATA, VARIABLE, ABSOLUTE),
HID_END_COLLECTION(LOGICAL),
HID_REPORT_COUNT(2),
HID_INPUT(DATA, ARRAY, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION),
HID_END_COLLECTION(APPLICATION),
};
static const uint8_t Debug_ReportDescriptor[] = {
// Report for sending the enter key
HID_USAGE_PAGE(GENERIC_DESKTOP),
HID_USAGE(JOYSTICK),
HID_USAGE(KEYBOARD),
HID_COLLECTION(APPLICATION),
HID_REPORT_ID(HID_REPORT_ID_ENTER),
HID_REPORT_ID(HID_REPORT_ID_DEBUG_A),
HID_USAGE(POINTER),
HID_COLLECTION(LOGICAL),
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),
HID_USAGE_PAGE(KEYBOARD),
HID_LOGICAL_MINIMUM(1, 0),
HID_LOGICAL_MAXIMUM(4, 0xffff),
HID_PHYSICAL_MINIMUM(1, 0),
HID_PHYSICAL_MAXIMUM(4, 0xffff),
HID_REPORT_COUNT(16),
HID_REPORT_SIZE(16),
HID_INPUT(DATA, VARIABLE, ABSOLUTE),
HID_END_COLLECTION(LOGICAL),
HID_REPORT_ID(HID_REPORT_ID_DEBUG_B),
HID_USAGE(POINTER),
HID_COLLECTION(LOGICAL),
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),
HID_LOGICAL_MINIMUM(1, 0),
HID_LOGICAL_MAXIMUM(4, 0xffff),
HID_PHYSICAL_MINIMUM(1, 0),
HID_PHYSICAL_MAXIMUM(4, 0xffff),
HID_REPORT_SIZE(16),
HID_REPORT_COUNT(16),
HID_INPUT(DATA, VARIABLE, ABSOLUTE),
HID_END_COLLECTION(LOGICAL),
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),
};
@ -291,7 +198,7 @@ static const config_desc_t gConfigDescriptor = {
_USBD_ITF_MAX,
0x01,
0x00,
0x80 | (USBD_SELF_POWERED << 6) | (USBD_REMOTE_WAKEUP << 5),
0x80 | USBD_REMOTE_WAKEUP_Msk,
USBD_MAX_POWER,
},
@ -313,7 +220,7 @@ static const config_desc_t gConfigDescriptor = {
DESC_INTERFACE,
USBD_ITF_CDC_CMD,
0x00,
0x01,
1,
USB_CLASS_CDC,
CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL,
CDC_COMM_PROTOCOL_NONE,
@ -389,7 +296,7 @@ static const config_desc_t gConfigDescriptor = {
DESC_INTERFACE,
USBD_ITF_HID_IO4,
0x00,
0x01,
1,
USB_CLASS_HID,
0,
HID_KEYBOARD,
@ -419,7 +326,7 @@ static const config_desc_t gConfigDescriptor = {
DESC_INTERFACE,
USBD_ITF_HID_MISC,
0x00,
0x01,
2,
USB_CLASS_HID,
0,
HID_KEYBOARD,
@ -452,12 +359,11 @@ static const config_desc_t gConfigDescriptor = {
},
};
const char* gszVendorInitial = "Bottersnike";
const char* gszVendor = IO4_VENDOR;
const char* gszProduct = "TASOLLER";
const usb_device_descr_t *gpDeviceDescriptor = &gIO4DeviceDescriptor;
const usb_desc_config_t *gpConfigDescriptor = &gConfigDescriptor.Config;
const usb_device_descr_t* gpDeviceDescriptor = &gIO4DeviceDescriptor;
const usb_desc_config_t* gpConfigDescriptor = &gConfigDescriptor.Config;
const uint32_t gu32HidDescIO4Offset = (offsetof(config_desc_t, HID_IO4));
const uint32_t gu32HidDescMiscOffset = (offsetof(config_desc_t, HID_Misc));
const uint32_t gu32UsbHidIO4ReportLen = sizeof IO4_ReportDescriptor;

View File

@ -51,7 +51,6 @@ void FMC_EEPROM_Load(void) {
if (gConfig.u32Magic != DATAFLASH_MAGIC) {
// Zeroing flags first means GCC knows we don't care about the other bits
gConfig.u8Flags = 0;
gConfig.bEnableIO4 = 1;
gConfig.bEnableKeyboard = 0;
gConfig.bEnableRainbow = 1;
@ -59,6 +58,7 @@ void FMC_EEPROM_Load(void) {
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.u16HueGroundActive = 330;

View File

@ -34,7 +34,7 @@ typedef struct __attribute__((aligned(4), packed)) {
// Flags
union {
struct __packed {
uint8_t bEnableIO4 : 1;
uint8_t bRsv00 : 1;
uint8_t bEnableKeyboard : 1;
uint8_t bEnableRainbow : 1;
};

301
src/hid.c
View File

@ -1,144 +1,189 @@
#include "tasoller.h"
#define HID_IO4_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_IO4_IN)))
#define HID_MISC_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_MISC_IN)))
uint16_t u16RequestedConsumerControl = 0;
uint32_t u32EnterPressStarted = 0xFFFFFFFF;
#define ENTER_HOLD_TIME 5000 // 5 seconds
#define HID_MISC_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_MISC_IN)))
#define HID_IO4_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_IO4_IN)))
#define HID_MISC_SEND(buf) \
do { \
USBD_SET_PAYLOAD_LEN(EP_HID_MISC_IN, sizeof *(buf)); \
gu8HIDMiscReady = 0; \
} while (0)
static const uint8_t u8GroundThreshold = 20;
static const uint8_t u8GroundKeymap[32] = {
KEY_I, KEY_COMMA, //
KEY_8, KEY_K, //
KEY_U, KEY_M, //
KEY_7, KEY_J, //
KEY_Y, KEY_N, //
KEY_6, KEY_H, //
KEY_T, KEY_B, //
KEY_5, KEY_G, //
KEY_R, KEY_V, //
KEY_4, KEY_F, //
KEY_E, KEY_C, //
KEY_3, KEY_D, //
KEY_W, KEY_X, //
KEY_2, KEY_S, //
KEY_Q, KEY_Z, //
KEY_1, KEY_A, //
KEY_I, KEY_9, // Dao uses: KEY_I, KEY_COMMA
KEY_8, KEY_K, //
KEY_U, KEY_M, //
KEY_7, KEY_J, //
KEY_Y, KEY_N, //
KEY_6, KEY_H, //
KEY_T, KEY_B, //
KEY_5, KEY_G, //
KEY_R, KEY_V, //
KEY_4, KEY_F, //
KEY_E, KEY_C, //
KEY_3, KEY_D, //
KEY_W, KEY_X, //
KEY_2, KEY_S, //
KEY_Q, KEY_Z, //
KEY_1, KEY_A, //
};
static const uint8_t u8AirKeymap[6] = {
HID_KEYBOARD_SLASH_AND_QUESTION_MARK, // VK_OEM_2
HID_KEYBOARD_PERIOD_AND_GREATER_THAN, // VK_OEM_PERIOD
HID_KEYBOARD_QUOTE_AND_DOUBLEQUOTE, // VK_OEM_7
HID_KEYBOARD_SEMICOLON_AND_COLON, // VK_OEM_1
HID_KEYBOARD_RIGHT_BRACKET_AND_RIGHT_CURLY_BRACE, // VK_OEM_6
HID_KEYBOARD_LEFT_BRACKET_AND_LEFT_CURLY_BRACE, // VK_OEM_4
// Dao mapping:
// HID_KEYBOARD_SLASH_AND_QUESTION_MARK, // VK_OEM_2
// HID_KEYBOARD_PERIOD_AND_GREATER_THAN, // VK_OEM_PERIOD
// HID_KEYBOARD_QUOTE_AND_DOUBLEQUOTE, // VK_OEM_7
// HID_KEYBOARD_SEMICOLON_AND_COLON, // VK_OEM_1
// HID_KEYBOARD_RIGHT_BRACKET_AND_RIGHT_CURLY_BRACE, // VK_OEM_6
// HID_KEYBOARD_LEFT_BRACKET_AND_LEFT_CURLY_BRACE, // VK_OEM_4
// UMIGURI mapping:
KEY_0, KEY_O, KEY_L, KEY_P, KEY_COMMA, KEY_PERIOD,
};
static inline void _HID_Keyboard_Tick(uint8_t bReal) {
hid_report_t *buf = (hid_report_t *)HID_MISC_BUF;
static uint8_t _HID_Keyboard_Tick(uint8_t bReleaseAll) {
hid_kbd_report_t *buf = (hid_kbd_report_t *)HID_MISC_BUF;
// Send a report of zeroes to release all keys
if (bReleaseAll) {
memset(buf, 0, sizeof *buf);
buf->bReportId = HID_REPORT_ID_KEYBOARD;
HID_MISC_SEND(buf);
return 1;
}
static uint8_t u8LastButtons = 0;
static uint32_t u32LastSlider = 0;
// If nothing changed, do nothing
if (gu8DigitalButtons == u8LastButtons && gu32PSoCDigital == u32LastSlider) {
return 0;
}
memset(buf, 0, sizeof *buf);
buf->bReportId = HID_REPORT_ID_KEYBOARD;
uint8_t kI = 0;
if (bReal) {
if (gu8DigitalButtons & 0x01) buf->bKeyboard[kI++] = HID_KEYBOARD_F1;
if (gu8DigitalButtons & 0x02) buf->bKeyboard[kI++] = HID_KEYBOARD_F2;
if (gu8DigitalButtons & 0x04) buf->bKeyboard[kI++] = u8AirKeymap[0];
if (gu8DigitalButtons & 0x08) buf->bKeyboard[kI++] = u8AirKeymap[1];
if (gu8DigitalButtons & 0x10) buf->bKeyboard[kI++] = u8AirKeymap[2];
if (gu8DigitalButtons & 0x20) buf->bKeyboard[kI++] = u8AirKeymap[3];
if (gu8DigitalButtons & 0x40) buf->bKeyboard[kI++] = u8AirKeymap[4];
if (gu8DigitalButtons & 0x80) buf->bKeyboard[kI++] = u8AirKeymap[5];
if (gu8DigitalButtons & DIGITAL_FN2_Msk) buf->bKeyboard[kI++] = HID_KEYBOARD_F1;
if (gu8DigitalButtons & DIGITAL_FN1_Msk) buf->bKeyboard[kI++] = HID_KEYBOARD_F2;
if (gu8DigitalButtons & 0x04) buf->bKeyboard[kI++] = u8AirKeymap[0];
if (gu8DigitalButtons & 0x08) buf->bKeyboard[kI++] = u8AirKeymap[1];
if (gu8DigitalButtons & 0x10) buf->bKeyboard[kI++] = u8AirKeymap[2];
if (gu8DigitalButtons & 0x20) buf->bKeyboard[kI++] = u8AirKeymap[3];
if (gu8DigitalButtons & 0x40) buf->bKeyboard[kI++] = u8AirKeymap[4];
if (gu8DigitalButtons & 0x80) buf->bKeyboard[kI++] = u8AirKeymap[5];
for (int i = 0; i < 32; i++) {
if (gu8GroundData[i] > u8GroundThreshold) buf->bKeyboard[kI++] = u8GroundKeymap[i];
}
for (int i = 0; i < 32; i++) {
if (gu32PSoCDigital & (1 << i)) buf->bKeyboard[kI++] = u8GroundKeymap[i];
}
USBD_SET_PAYLOAD_LEN(EP_HID_MISC_IN, sizeof *buf);
HID_MISC_SEND(buf);
u8LastButtons = gu8DigitalButtons;
u32LastSlider = gu32PSoCDigital;
return 1;
}
static uint8_t _HID_Consumer_Tick(void) {
static uint16_t u16Last = 0;
if (u16Last == u16RequestedConsumerControl) return 0;
u16Last = u16RequestedConsumerControl;
#define IO4_BUTTON_TEST (1 << 9)
#define IO4_BUTTON_SERVICE (1 << 6)
#define IO4_CMD_SET_COMM_TIMEOUT 0x01
#define IO4_CMD_SET_SAMPLING_COUNT 0x02
#define IO4_CMD_CLEAR_BOARD_STATUS 0x03
#define IO4_CMD_SET_GENERAL_OUTPUT 0x04
#define IO4_CMD_SET_PWM_OUTPUT 0x05
#define IO4_CMD_SET_UNIQUE_OUTPUT 0x41
#define IO4_CMD_UPDATE_FIRMWARE 0x85
volatile uint8_t u8IO4SystemStatus = 0;
volatile uint8_t u8IO4USBStatus = 0;
volatile uint16_t u16IO4CommTimeout = 0;
volatile uint8_t u8IO4SamplingCount = 0;
static void _HID_IO4_Prepare(volatile uint8_t *pu8EpBuf) {
io4_hid_in_t *buf = (io4_hid_in_t *)pu8EpBuf;
hid_consumer_report_t *buf = (hid_consumer_report_t *)HID_MISC_BUF;
memset(buf, 0, sizeof *buf);
buf->bReportId = HID_REPORT_ID_IO4;
// System buttons
if (gu8DigitalButtons & 0x01) buf->wButtons[0] |= IO4_BUTTON_TEST;
if (gu8DigitalButtons & 0x02) buf->wButtons[0] |= IO4_BUTTON_SERVICE;
// Airs
if (!(gu8DigitalButtons & 0x04)) buf->wButtons[0] |= 1 << 13;
if (!(gu8DigitalButtons & 0x08)) buf->wButtons[1] |= 1 << 13;
if (!(gu8DigitalButtons & 0x10)) buf->wButtons[0] |= 1 << 12;
if (!(gu8DigitalButtons & 0x20)) buf->wButtons[1] |= 1 << 12;
if (!(gu8DigitalButtons & 0x40)) buf->wButtons[0] |= 1 << 11;
if (!(gu8DigitalButtons & 0x80)) buf->wButtons[1] |= 1 << 11;
buf->bUsbStatus = u8IO4USBStatus;
buf->bSystemStatus = u8IO4SystemStatus;
buf->bReportId = HID_REPORT_ID_CONSUMER_CONTROL;
buf->u16Control[0] = u16RequestedConsumerControl;
HID_MISC_SEND(buf);
return 1;
}
static void _HID_IO4_Tick() {
_HID_IO4_Prepare(HID_IO4_BUF);
// We must send data every 8ms! None of that "only sending changed keys" stuff
// Trigger a write
gu8HIDIO4Ready = 0;
USBD_SET_PAYLOAD_LEN(EP_HID_IO4_IN, sizeof(io4_hid_in_t));
}
static void _HID_Debug_Tick() {
debug_hid_report_t *buf = (debug_hid_report_t *)HID_MISC_BUF;
memset(buf, 0, sizeof *buf);
static uint8_t _HID_Enter_Tick(void) {
// TODO: This isn't working, so we're using su8LastState for now instead
if (u32EnterPressStarted == 0xFFFFFFFF) return 0;
static uint8_t bWhich = 0;
if ((bWhich++) & 1) {
buf->bReportId = HID_REPORT_ID_DEBUG_A;
for (uint8_t i = 0; i < 32; i += 2) buf->wData[i / 2] = gu8GroundData[i];
static uint8_t su8LastState = 0;
uint8_t u8State = 0;
// There's an _incredibly_ small chance the user tapped the cell at exactly the ms (49 days in!)
// when the timer wrapped round to 0. This is too stupid to account for.
if (u32EnterPressStarted && (MS_SINCE(u32EnterPressStarted) < ENTER_HOLD_TIME)) {
u8State = 1;
} else {
buf->bReportId = HID_REPORT_ID_DEBUG_B;
for (uint8_t i = 1; i < 32; i += 2) buf->wData[i / 2] = gu8GroundData[i];
u8State = 0;
u32EnterPressStarted = 0xFFFFFFFF;
}
USBD_SET_PAYLOAD_LEN(EP_HID_MISC_IN, sizeof *buf);
if (u8State == su8LastState) return 0;
su8LastState = u8State;
hid_enter_report_t *buf = (hid_enter_report_t *)HID_MISC_BUF;
memset(buf, 0, sizeof *buf);
buf->bReportId = HID_REPORT_ID_ENTER;
if (u8State) buf->u8Keyboard[0] = KEY_ENTER;
HID_MISC_SEND(buf);
return 1;
}
static void _HID_Misc_Tick() {
typedef enum {
TIMESLOT_KEYBOARD = 0,
TIMESLOT_CONSUMER,
TIMESLOT_ENTER,
_TIMESLOT_COUNT,
} eTimeslot_t;
static void _HID_Misc_Tick(void) {
static uint8_t sbLastEnableKeyboard = 0;
if (gConfig.bEnableKeyboard) {
sbLastEnableKeyboard = 1;
_HID_Keyboard_Tick(1);
} else if (sbLastEnableKeyboard) {
// If we've just disabled the keyboard, make sure to send a packet with all keys released!
sbLastEnableKeyboard = 0;
_HID_Keyboard_Tick(0);
}
// TODO: gbEnableDebug (we'll need to use a toggle to alternate)
gu8HIDMiscReady = 0;
// We have multiple things we're going to be sending over this HID endpoint, so we timeshare
// which reports are sent. If a particular report has nothing to report in its slot, the next
// report gets a chance instead.
static eTimeslot_t eTimeslot = 0;
uint8_t u8Tries = _TIMESLOT_COUNT;
while (u8Tries--) {
switch (eTimeslot++) {
case TIMESLOT_KEYBOARD:
if (gConfig.bEnableKeyboard) {
sbLastEnableKeyboard = 1;
if (_HID_Keyboard_Tick(0)) goto timeslot_used;
} else if (sbLastEnableKeyboard) {
// If we've just disabled the keyboard, make sure to send a packet with all keys
// released!
sbLastEnableKeyboard = 0;
if (_HID_Keyboard_Tick(1)) goto timeslot_used;
}
break;
case TIMESLOT_CONSUMER:
if (_HID_Consumer_Tick()) goto timeslot_used;
break;
case TIMESLOT_ENTER:
if (_HID_Enter_Tick()) goto timeslot_used;
break;
default:
break;
}
if (eTimeslot > _TIMESLOT_COUNT) eTimeslot = 0;
}
timeslot_used:;
;
}
void USBD_HID_PrepareReport() {
if (gu8HIDIO4Ready) _HID_IO4_Tick();
void USBD_HID_PrepareReport(void) {
if (gu8HIDIO4Ready) IO4_HID_Tick();
if (gu8HIDMiscReady) _HID_Misc_Tick();
}
static uint8_t sIO4InBuffer[sizeof(io4_hid_in_t)] = { 0 };
uint8_t *USBD_HID_GetReport(uint8_t u8ReportId, uint32_t *pu32Size) {
switch (u8ReportId) {
case HID_REPORT_ID_IO4:
if (!gConfig.bEnableIO4) return NULL;
_HID_IO4_Prepare(sIO4InBuffer);
IO4_HID_Prepare(sIO4InBuffer);
*pu32Size = sizeof sIO4InBuffer;
return sIO4InBuffer;
default:
@ -148,51 +193,7 @@ uint8_t *USBD_HID_GetReport(uint8_t u8ReportId, uint32_t *pu32Size) {
void USBD_HID_SetReport(volatile uint8_t *pu8EpBuf, uint32_t u32Size) {
// TODO: is pu8EpBuf[0] the report ID?
// We need to switch on that report ID so we know what we're doing!
if (!gConfig.bEnableIO4) return;
if (u32Size < 2) return;
switch (pu8EpBuf[1]) {
case IO4_CMD_SET_COMM_TIMEOUT:
if (u32Size >= 2 + 1) {
u16IO4CommTimeout = (uint16_t)pu8EpBuf[2] * 200;
u8IO4SystemStatus |= 0x10;
gu8HIDIO4Ready = 1;
_HID_IO4_Tick();
}
break;
case IO4_CMD_SET_SAMPLING_COUNT:
if (u32Size >= 2 + 1) {
u8IO4SamplingCount = pu8EpBuf[2];
u8IO4SystemStatus |= 0x20;
gu8HIDIO4Ready = 1;
_HID_IO4_Tick();
}
break;
case IO4_CMD_CLEAR_BOARD_STATUS:
u8IO4SystemStatus &= 0x0F;
u8IO4USBStatus &= 0x04;
gu8HIDIO4Ready = 1;
_HID_IO4_Tick();
break;
case IO4_CMD_SET_GENERAL_OUTPUT:
if (u32Size >= 2 + 3) {
// 20 bits of data for GPO (+4 of padding)
}
break;
case IO4_CMD_SET_PWM_OUTPUT:
if (u32Size >= 2 + 0) {
// 0 bytes of data for PWM duty cycles (IO4 has no PWM!)
}
break;
case IO4_CMD_SET_UNIQUE_OUTPUT:
if (u32Size >= 2 + 62) {
// 62 bytes of unique output data
}
break;
case IO4_CMD_UPDATE_FIRMWARE:
break;
}
IO4_Control(pu8EpBuf[1], u32Size - 2, &pu8EpBuf[2]);
}

61
src/hid_def.h Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#include <stdint.h>
#include "_compiler.h"
// Triggers to start sending data
extern uint8_t volatile gu8HIDIO4Ready;
extern uint8_t volatile gu8HIDMiscReady;
extern uint16_t u16RequestedConsumerControl;
extern uint32_t u32EnterPressStarted;
enum {
HID_REPORT_ID_IO4 = 1,
HID_REPORT_ID_KEYBOARD,
HID_REPORT_ID_CONSUMER_CONTROL,
HID_REPORT_ID_ENTER,
HID_REPORT_ID_IO4_CMD = 16,
};
#define NUM_FN 2
#define NUM_AIR 6
#define NUM_GROUND 32
typedef struct __packed {
uint8_t bReportId;
uint8_t bKeyboard[NUM_FN + NUM_AIR + NUM_GROUND];
} hid_kbd_report_t;
typedef struct __packed {
uint8_t bReportId;
uint16_t u16Control[2];
} hid_consumer_report_t;
typedef struct __packed {
uint8_t bReportId;
uint8_t u8Keyboard[1];
} hid_enter_report_t;
typedef struct __packed {
uint8_t bReportId;
uint16_t wADC[8];
uint16_t wRotary[4];
uint16_t wCoin[2];
uint16_t wButtons[2];
uint8_t bSystemStatus;
uint8_t bUsbStatus;
uint8_t bUnique[29];
} io4_hid_in_t;
typedef struct __packed {
uint8_t bReportId;
uint8_t bCmd;
uint8_t bData[62];
} io4_hid_out_t;
// void HID_Tick();
void USBD_HID_PrepareReport();
uint8_t *USBD_HID_GetReport(uint8_t u8ReportId, uint32_t *pu32Size);
void USBD_HID_SetReport(volatile uint8_t *pu8EpBuf, uint32_t u32Size);

156
src/io4.c Normal file
View File

@ -0,0 +1,156 @@
#include "tasoller.h"
#define HID_IO4_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_IO4_IN)))
volatile uint8_t u8IO4SystemStatus = 0;
volatile uint8_t u8IO4USBStatus = 0;
volatile uint16_t u16IO4CommTimeout = 0;
volatile uint8_t u8IO4SamplingCount = 0;
volatile uint16_t u16IO4Coins[2];
volatile uint8_t gu8IO4PWMScale;
volatile uint8_t gu8IO4PWMOutput[6];
volatile uint8_t gu8IO4DigitalOutput[3]; // 20 bits
uint16_t gu16IO4ForceButtons = 0;
void IO4_HID_Prepare(volatile uint8_t *pu8EpBuf) {
io4_hid_in_t *buf = (io4_hid_in_t *)pu8EpBuf;
memset(buf, 0, sizeof *buf);
buf->bReportId = HID_REPORT_ID_IO4;
// System buttons
buf->wButtons[0] = gu16IO4ForceButtons;
// if (gu8DigitalButtons & DIGITAL_FN2_Msk) buf->wButtons[0] |= IO4_BUTTON_TEST;
// if (gu8DigitalButtons & DIGITAL_FN1_Msk) buf->wButtons[0] |= IO4_BUTTON_SERVICE;
// Airs
if (!(gu8DigitalButtons & 0x04)) buf->wButtons[0] |= 1 << 13;
if (!(gu8DigitalButtons & 0x08)) buf->wButtons[1] |= 1 << 13;
if (!(gu8DigitalButtons & 0x10)) buf->wButtons[0] |= 1 << 12;
if (!(gu8DigitalButtons & 0x20)) buf->wButtons[1] |= 1 << 12;
if (!(gu8DigitalButtons & 0x40)) buf->wButtons[0] |= 1 << 11;
if (!(gu8DigitalButtons & 0x80)) buf->wButtons[1] |= 1 << 11;
buf->bUsbStatus = u8IO4USBStatus;
buf->bSystemStatus = u8IO4SystemStatus;
// Pos-edge trigger
static uint8_t su8LastDigitalButtons = 0;
if ((gu8DigitalButtons & ~su8LastDigitalButtons) & DIGITAL_FN1_Msk) {
if (!IO4_GetCoinBlocker()) {
u16IO4Coins[0]++;
}
}
su8LastDigitalButtons = gu8DigitalButtons;
buf->wCoin[0] = BYTESWAP_U16(u16IO4Coins[0]);
buf->wCoin[1] = BYTESWAP_U16(u16IO4Coins[1]);
}
void IO4_HID_Tick(void) {
IO4_HID_Prepare(HID_IO4_BUF);
// We must send data every 8ms! None of that "only sending changed keys" stuff
// Trigger a write
gu8HIDIO4Ready = 0;
USBD_SET_PAYLOAD_LEN(EP_HID_IO4_IN, sizeof(io4_hid_in_t));
}
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) {
u16IO4CommTimeout = (uint16_t)pu8Buffer[0] * 200;
u8IO4SystemStatus |= 0x10;
gu8HIDIO4Ready = 1;
IO4_HID_Tick();
}
break;
case IO4_CMD_SET_SAMPLING_COUNT:
if (u32Size >= 1) {
u8IO4SamplingCount = pu8Buffer[0];
u8IO4SystemStatus |= 0x20;
gu8HIDIO4Ready = 1;
IO4_HID_Tick();
}
break;
case IO4_CMD_CLEAR_BOARD_STATUS:
u8IO4SystemStatus &= 0x0F;
u8IO4USBStatus &= 0x04;
gu8HIDIO4Ready = 1;
IO4_HID_Tick();
break;
case IO4_CMD_SET_GENERAL_OUTPUT:
// 20 bits of data for GPO (+4 bits of padding)
if (u32Size >= 3) {
gu8IO4DigitalOutput[0] = pu8Buffer[0];
gu8IO4DigitalOutput[1] = pu8Buffer[1];
gu8IO4DigitalOutput[2] = pu8Buffer[2];
}
break;
case IO4_CMD_SET_PWM_OUTPUT:
if (u32Size >= 0) {
// 0 bytes of data for PWM duty cycles (IO4 has no PWM!)
}
break;
case IO4_CMD_SET_UNIQUE_OUTPUT:
// 62 bytes of unique output data
if (u32Size >= 62) {
// [0]: Enable mask (bit 7=PWM1, 2=PWM6, 0~1 unused)
// [1]: Brightness scaler (1~256, with 0=256)
// [2]: PWM1 brightness (pin CN3.55)
// [3]: PWM2 brightness (pin CN3.56)
// [4]: PWM3 brightness (pin CN9.5 )
// [5]: PWM4 brightness (pin CN9.6 )
// [6]: PWM5 brightness (pin CN9.9 )
// [7]: PWM6 brightness (pin CN9.10)
// For maimai DX:
// [0]: 11111100 (Enable PWM1~6)
// [1]: 00
// [2]: 1P Billboard Red
// [3]: 2P Billboard Red
// [4]: 1P Billboard Green
// [5]: 2P Billboard Green
// [6]: 1P Billboard Blue
// [7]: 2P Billboard Blue
//
// For chunithm:
// [0]: 00111000 (Enable PWM3~5)
// [1]: 00
// [2]: Unused
// [3]: Unused
// [4]: Light gate Blue
// [5]: Light gate Red
// [6]: Light gate Green
// [7]: Unused
uint8_t u8Mask = pu8Buffer[0];
if (pu8Buffer[1] == 0)
gu8IO4PWMScale = 255;
else
gu8IO4PWMScale = pu8Buffer[1] - 1;
for (uint8_t i = 0; i < 6; i++) {
gu8IO4PWMOutput[i] = (u8Mask & (1 << (7 - i))) ? pu8Buffer[2 + i] : 0;
}
}
break;
case IO4_CMD_UPDATE_FIRMWARE:
break;
}
}

31
src/io4.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <stdint.h>
#define IO4_BUTTON_TEST (1 << 9)
#define IO4_BUTTON_SERVICE (1 << 6)
#define IO4_CMD_SET_COMM_TIMEOUT 0x01
#define IO4_CMD_SET_SAMPLING_COUNT 0x02
#define IO4_CMD_CLEAR_BOARD_STATUS 0x03
#define IO4_CMD_SET_GENERAL_OUTPUT 0x04
#define IO4_CMD_SET_PWM_OUTPUT 0x05
#define IO4_CMD_SET_UNIQUE_OUTPUT 0x41
#define IO4_CMD_84 0x84
#define IO4_CMD_UPDATE_FIRMWARE 0x85
#define IO4_CMD_88 0x88 // data[0] = D9
extern volatile uint8_t gu8IO4PWMScale;
extern volatile uint8_t gu8IO4PWMOutput[6];
extern volatile uint8_t gu8IO4DigitalOutput[3];
extern uint16_t gu16IO4ForceButtons;
static inline uint8_t IO4_GetCoinBlocker(void) {
// Chunithm's coin blocker is wired to OUT1
return (gu8IO4DigitalOutput[0] & 0x80) ? 0 : 1;
}
void IO4_HID_Prepare(volatile uint8_t *pu8EpBuf);
void IO4_HID_Tick(void);
void IO4_Control(uint8_t u8Cmd, uint32_t u32Size, volatile uint8_t *pu8Buffer);

582
src/led.c
View File

@ -1,61 +1,137 @@
#include "tasoller.h"
hsv_t gaControlledIntLedData[LED_NUM_GROUND] = { 0 };
hsv_t gaControlledIntLedData[LED_NUM_GROUND_LOGICAL] = { 0 };
uint8_t gbLedDataIsControlledInt = 0;
uint8_t gu8aControlledExtLedData[32 * 3];
rgb_t gaControlledExtLedData[32];
uint8_t gbLedDataIsControlledExt = 0;
uint8_t gbLedIsCustom = 0;
volatile uint8_t gu8LEDTx[LED_Tx_BUFFER];
static void (*s_I2C1HandlerFn)(uint32_t u32Status) = NULL;
volatile uint8_t gu8LEDTx[LED_PACKET_MAX_SIZE];
volatile static uint8_t su8LedTxDataLock = 0;
typedef enum : uint8_t {
I2C_SLAVE_TX_REPEAT_START_STOP = 0xA0,
I2C_SLAVE_TX_ADDR_ACK = 0xA8,
I2C_SLAVE_TX_ARBITRATION_LOST = 0xB0,
I2C_SLAVE_TX_DATA_ACK = 0xB8,
I2C_SLAVE_TX_DATA_NACK = 0xC0,
I2C_SLAVE_TX_LAST_DATA_ACK = 0xC8,
I2C_SLAVE_RX_ADDR_ACK = 0x60,
I2C_SLAVE_RX_ARBITRATION_LOST = 0x68,
I2C_SLAVE_RX_DATA_ACK = 0x80,
I2C_SLAVE_RX_DATA_NACK = 0x88,
} I2C_Status_Slave;
// Helper definitions
#define SLAVE_RX_ADDR_ACK 0x60
#define SLAVE_RX_ACK 0x80
#define I2C_SLAVE_RX_NACK 0x88
#define I2C_SLAVE_TX_REPEAT_START_STOP 0xA0
#define SLAVE_TX_ACK 0xA8
#define I2C_SLAVE_TX_NACK 0xC0
volatile uint8_t* gpu8I2CRx = NULL;
volatile uint16_t u16I2CRxIndex = 0;
void I2C1_SlaveTx(I2C_Status_Slave eStatus) {
if (!eStatus) {
// Something went very wrong; restart the I2C controller
I2C_Close(I2C1);
LED_I2C1_Init();
return;
}
static uint8_t u8Cmd = 0;
static uint16_t su16I2CReadAddr = 0;
/**
* Bulk data receive request:
* (B0)Rx: START+SLA+W
* (B1)Tx: ACK
* (B2)Rx: [u8Cmd]
* (B3)Tx: ACK
* (B4)Rx: (Repeat START)+SLA+R
* (B5)Tx: ACK
* (B6)Rx: ACK
* (B7)Tx: [u8Data[i]] |
* (B8)Rx: ACK | Looped until Rx:NACK
* Rx: STOP
*
* Single data receive request:
* (S0)Rx: START+SLA+W
* (S1)Tx: ACK
* (S2)Rx: [u8Cmd]
* (S3)Tx: ACK
* Rx: STOP
* ---
* (S4)Rx: START+SLA+R
* (S5)Tx: ACK
*/
switch (eStatus) {
// === Receive address and command ===
case I2C_SLAVE_RX_ADDR_ACK: // (B0,S0)
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); // (B1,S1)
break;
case I2C_SLAVE_RX_DATA_ACK: // (B2,S2)
uint8_t u8Data = I2C_GET_DATA(I2C1);
if (u16I2CRxIndex == 0) {
u8Cmd = u8Data;
switch (u8Cmd) {
case LED_I2C_REG_PACKET:
// The master is requesting a read-out of all the data we have in our
// buffer.
su16I2CReadAddr = 0;
break;
default:
// If we don't recognise this command, treat it as a register read.
// (Back-compat with stock LED firmware)
su16I2CReadAddr = u8Cmd;
break;
}
u16I2CRxIndex++;
} else {
// TODO: Currently we don't expose u8Cmd anywhere
if (gpu8I2CRx != NULL) {
gpu8I2CRx[u16I2CRxIndex - 1] = u8Data;
// TODO: Have some bounds checking, and NACK an out of bounds write
}
u16I2CRxIndex++;
}
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); // (B3,S3)
break;
case I2C_SLAVE_RX_DATA_NACK:
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
u16I2CRxIndex = 0;
break;
case I2C_SLAVE_TX_REPEAT_START_STOP: // (B4)
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); // (B5)
u16I2CRxIndex = 0;
break;
// === Transmit our data ===
case I2C_SLAVE_TX_ADDR_ACK: // (B6)
case I2C_SLAVE_TX_DATA_ACK: // We got an ACK, and need to continue
I2C_SET_DATA(I2C1, gu8LEDTx[su16I2CReadAddr++]); // (B7)
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
break;
case I2C_SLAVE_TX_LAST_DATA_ACK: // We got an ACK, but it's time to stop
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI);
u16I2CRxIndex = 0;
break;
case I2C_SLAVE_TX_DATA_NACK: // We got a NACK; master has read enough data
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
u16I2CRxIndex = 0;
break;
// === Error cases ===
case I2C_SLAVE_RX_ARBITRATION_LOST: // SLA+W
case I2C_SLAVE_TX_ARBITRATION_LOST: // SLA+R
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
u16I2CRxIndex = 0;
break;
}
}
void I2C1_IRQHandler(void) {
if (I2C_GET_TIMEOUT_FLAG(I2C1)) {
I2C_ClearTimeoutFlag(I2C1);
} else {
if (s_I2C1HandlerFn != NULL) (s_I2C1HandlerFn)(I2C1->I2CSTATUS);
}
}
void I2C1_SlaveTx(uint32_t u32Status) {
static uint8_t su8I2CReadAddr = 0;
switch (u32Status) {
case SLAVE_RX_ACK:
su8I2CReadAddr = I2C1->I2CDAT;
su8LedTxDataLock = 1;
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
break;
case SLAVE_TX_ACK:
I2C_SET_DATA(I2C1, gu8LEDTx[su8I2CReadAddr]);
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
break;
case SLAVE_RX_ADDR_ACK:
case I2C_SLAVE_TX_NACK:
case I2C_SLAVE_RX_NACK:
case I2C_SLAVE_TX_REPEAT_START_STOP:
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
break;
default:
// Hmm?
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
break;
I2C1_SlaveTx(I2C1->I2CSTATUS);
}
}
void LED_I2C1_Init(void) {
I2C_Open(I2C1, 100000);
I2C_Open(I2C1, 400 kHz);
I2C_SetSlaveAddr(I2C1, 0, 0x18, 0);
I2C_SetSlaveAddr(I2C1, 1, 0x30, 0);
I2C_SetSlaveAddr(I2C1, 2, 0x55, 0);
@ -69,344 +145,118 @@ void LED_I2C1_Init(void) {
// I2C1 enter no address SLV mode
I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA);
s_I2C1HandlerFn = I2C1_SlaveTx;
}
// static inline void LEDTxLock(void) {
// gu8LEDTx[0] = 0;
// }
// static inline void LEDTxCommit(uint8_t u8Command, uint8_t u8NExpected) {
// gu8LEDTx[0] = u8Command;
// }
static const uint8_t _LED_GroundBrightness(void) {
if (gbLedDataIsControlledInt) return gConfig.u8LedGroundBrightness;
if (g_u8UsbState == USB_STATE_SUSPEND && gu32NowMs > 5000) return 0;
if (gbLedDataIsControlledExt) {
// The game is going to tell us how bright it wants the LEDs
// For chunithm, that's 40/63 = 63.5% brightness.
// TODO: Do we actually want to do this? Chunithm has no way for operators to change it
// TODO: This won't be reflected in gu8LEDTx[1] with our custom firmware
/**
* @brief Convert from RGB to HSV
*
* @param pu8aRGB Destination 3-tuple to receive RGB values
* @param u16H Hue, ranging 0~LED_HUE_MAX
* @param u8S Saturation, ranging 0~255
* @param u8V Value, ranging 0~255
*/
void HsvToRgb(uint8_t* pu8aRGB, uint16_t u16H, uint8_t u8S, uint8_t u8V) {
if (u8S == 0) {
pu8aRGB[0] = u8V;
pu8aRGB[1] = u8V;
pu8aRGB[2] = u8V;
return;
// The real range for this value is 0~63
// Chunithm will always be sending a constant value of 40 though, as far as I'm aware.
// Because of that, if we performed the scaling we'd be getting 40/63 = 63.5% brightness.
// Instead, we're only going to scale on the off-chance that the brightness is actually
// changed and it goes below 40.
// During startup the brightness is set to 0, but all LEDs are zeroed too so... :D
if (gu8GameBrightness < 40)
return ((uint16_t)gConfig.u8LedGroundBrightness * (uint16_t)gu8GameBrightness) / 63;
return gConfig.u8LedGroundBrightness;
}
uint8_t region = u16H / (LED_HUE_MAX / 6);
uint8_t remainder = (u16H - (region * (LED_HUE_MAX / 6))) * (255 / (LED_HUE_MAX / 6));
uint8_t p = (u8V * (255 - u8S)) >> 8;
uint8_t q = (u8V * (255 - ((u8S * remainder) >> 8))) >> 8;
uint8_t t = (u8V * (255 - ((u8S * (255 - remainder)) >> 8))) >> 8;
switch (region) {
case 0:
pu8aRGB[0] = u8V;
pu8aRGB[1] = t;
pu8aRGB[2] = p;
break;
case 1:
pu8aRGB[0] = q;
pu8aRGB[1] = u8V;
pu8aRGB[2] = p;
break;
case 2:
pu8aRGB[0] = p;
pu8aRGB[1] = u8V;
pu8aRGB[2] = t;
break;
case 3:
pu8aRGB[0] = p;
pu8aRGB[1] = q;
pu8aRGB[2] = u8V;
break;
case 4:
pu8aRGB[0] = t;
pu8aRGB[1] = p;
pu8aRGB[2] = u8V;
break;
default:
pu8aRGB[0] = u8V;
pu8aRGB[1] = p;
pu8aRGB[2] = q;
break;
}
return;
return gConfig.u8LedGroundBrightness;
}
static const uint8_t _LED_WingBrightness(void) {
if (gbLedDataIsControlledInt) return gConfig.u8LedWingBrightness;
if (g_u8UsbState == USB_STATE_SUSPEND && gu32NowMs > 5000) return 0;
if (gbLedDataIsControlledExt)
return ((uint16_t)gConfig.u8LedGroundBrightness * (uint16_t)gu8IO4PWMScale) / 255;
return gConfig.u8LedWingBrightness;
}
// 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)
// 0x1X: [CFW] Rainbow with black gaps. X bars black (from right)
// 0x20: [CFW] Flashes three times
// 0x80: [Stock] Flashes three times
static uint8_t su8LedSpecial = 0x05;
// 0: Separator bar every 4 (4 sections)
// 1: Separator bar every 2 (8 sections)
// 2: Separator bar every 1 (16 sections)
// 3: No separator bars
// 4~7: LEDs off
// For some reason this value can be |8, even though firmware suggests otherwise
static uint8_t su8LedSeparators = 1;
// 0: Invalid, but functions as 1
// 1: 32-key mode
// 2: 16-key mode (same as 32key mode)
// 3: 8-key mode (bars light in pairs)
// 4: 4-key mode (bars light in quads)
static uint8_t su8LedNKey = 1;
// 0x40: Turns off ground LEDs
// 0x80: Turns off wing LEDs
static uint8_t su8LedOff = 0;
// 0x8X: Separator 1~(X+1) lit (ie X ranges from 0~E; F is the same as E)
static uint8_t su8LedCfwRainbow = 0x8F;
void LED_WriteBasicGrounds(void) {
gu8LEDTx[0] = LED_CMD_BASIC;
// 32 bits of grounds
// (01,02)=key1, (04,08)=key2 (10,20)=key3, (40,80)=key4
gu8LEDTx[1] = 0;
gu8LEDTx[2] = 0;
gu8LEDTx[3] = 0;
gu8LEDTx[4] = 0;
for (uint8_t i = 0; i < 4; i++) {
for (uint8_t j = 0; j < 8; j++) {
if (gu8GroundData[i * 8 + j] > PSoC_INTERNAL_DIGITAL_TH) gu8LEDTx[i + 1] |= (1 << j);
}
}
#ifdef LED_FIRMWARE_CFW
gu8LEDTx[5] = 0; // Wing fill
for (uint8_t i = 0; i < 6; i++)
if (gu8DigitalButtons & (1 << (i + 2))) gu8LEDTx[5] |= 1 << i;
gu8LEDTx[6] = su8LedCfwRainbow;
gu8LEDTx[7] = 0; // Unused
#else
// Wings
gu8LEDTx[5] = 0; // Wing fill
for (uint8_t i = 0; i < 6; i++)
if (gu8DigitalButtons & (1 << (i + 2))) gu8LEDTx[5] |= 1 << i;
gu8LEDTx[6] = gConfig.u16HueWingLeft / LED_HUE_SCALE; // Hue left (default 330)
gu8LEDTx[7] = gConfig.u16HueWingRight / LED_HUE_SCALE; // Hue right (default 180)
#endif
// Unused in CFW
gu8LEDTx[8] = gConfig.u16HueGround / LED_HUE_SCALE; // Hue ground inactive (default 45)
gu8LEDTx[9] = gConfig.u16HueGroundActive / LED_HUE_SCALE; // Hue ground active (default 330)
// In CFW only su8LedOff is respected
gu8LEDTx[10] = (su8LedSeparators << 4) | su8LedOff | su8LedNKey;
gu8LEDTx[11] = su8LedSpecial;
static inline void _LED_SetPower(void) {
PIN_LED_GROUND_PWR = _LED_GroundBrightness() ? 1 : 0;
PIN_LED_WING_PWR = _LED_WingBrightness() ? 1 : 0;
}
static const uint8_t su8aWingSensors[LED_NUM_LEFT] = {
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, //
DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, //
DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, //
DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, //
};
void LED_Write(void) {
Pled_rx_custom_rgb pTxRGB = (Pled_rx_custom_rgb)gu8LEDTx;
Pled_rx_custom_hsv pTxHSV = (Pled_rx_custom_hsv)gu8LEDTx;
Pled_rx_custom_mixed pTxMix = (Pled_rx_custom_mixed)gu8LEDTx;
// We might not use all of these (we aren't using RGB at the moment!) but they're just
// convenience aliases.
(void)pTxRGB;
(void)pTxHSV;
(void)pTxMix;
#define SATURATION_ACTIVE 255
#define SATURATION_INACTIVE 240
#define VALUE_ACTIVE (gConfig.u8LedWingBrightness)
#define VALUE_INACTIVE (gConfig.u8LedWingBrightness / 2)
static void LED_OffGround(void) {
memset((uint8_t*)&gu8LEDTx[LED_DATA_OFFSET], 0, LED_NUM_GROUND * 3);
}
static void LED_OffWings(void) {
memset((uint8_t*)&gu8LEDTx[LED_DATA_OFFSET + LED_NUM_GROUND * 3], 0,
(LED_NUM_LEFT + LED_NUM_RIGHT) * 3);
}
static void LED_AirWings(void) {
uint8_t u8aRgbActive[3];
uint8_t u8aRgbInactive[3];
uint8_t j, i = LED_NUM_GROUND;
// Left wing
HsvToRgb(u8aRgbActive, gConfig.u16HueWingLeft, SATURATION_ACTIVE, VALUE_ACTIVE);
HsvToRgb(u8aRgbInactive, gConfig.u16HueWingLeft, SATURATION_INACTIVE, VALUE_INACTIVE);
for (j = 0; i < LED_NUM_GROUND + LED_NUM_LEFT; i++, j++) {
// GRB
if (gu8DigitalButtons & su8aWingSensors[LED_NUM_LEFT - j - 1]) {
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRgbActive[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRgbActive[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRgbActive[2];
} else {
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRgbInactive[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRgbInactive[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRgbInactive[2];
}
}
// Right wing
HsvToRgb(u8aRgbActive, gConfig.u16HueWingRight, SATURATION_ACTIVE, VALUE_ACTIVE);
HsvToRgb(u8aRgbInactive, gConfig.u16HueWingRight, SATURATION_INACTIVE, VALUE_INACTIVE);
for (j = 0; i < LED_NUM_GROUND + LED_NUM_LEFT + LED_NUM_RIGHT; i++, j++) {
// GRB
if (gu8DigitalButtons & su8aWingSensors[j]) {
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRgbActive[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRgbActive[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRgbActive[2];
} else {
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRgbInactive[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRgbInactive[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRgbInactive[2];
}
}
}
static uint8_t LED_ScaleU8(uint8_t u8V, uint8_t u8Scale) {
return ((uint16_t)u8V * (uint16_t)u8Scale) / 255;
}
static void LED_GroundRainbow(void) {
uint8_t u8aRGB[3];
// 5 ticks * 360 hue = 1800 calls for one cycle (1.8s)
static uint16_t u16Hue = 0;
static uint8_t u8Ticker = 0;
if (++u8Ticker == 5) {
u8Ticker = 0;
u16Hue++;
if (u16Hue == LED_HUE_MAX) u16Hue = 0;
}
/**
* There are 48 LEDs for ground, but we only send 31 values
* They're mapped to the LEDs as follows:
* 00a11b22c33d44f55g66h77i...
*
* That is, we can't split-colour a key :P
*/
for (uint8_t i = 0; i < LED_NUM_GROUND; i++) {
uint8_t v = 190;
uint8_t h = 0;
uint8_t nCell = i >> 1;
if (i % 2 == 0) {
if (gu16PSoCDigital & (1 << nCell)) {
v = 255;
h = LED_HUE_MAX / 2;
}
} else if (nCell % 4 == 3) {
h = LED_HUE_MAX / 2;
}
// GRB
HsvToRgb(u8aRGB, (u16Hue + h + (i * (LED_HUE_MAX / LED_NUM_GROUND))) % LED_HUE_MAX, v,
LED_ScaleU8(v - 63, gConfig.u8LedGroundBrightness));
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRGB[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRGB[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRGB[2];
}
}
static void LED_GroundStatic(void) {
uint8_t u8aGround[3];
uint8_t u8aGroundActive[3];
HsvToRgb(u8aGround, gConfig.u16HueGround, 255, gConfig.u8LedGroundBrightness);
HsvToRgb(u8aGroundActive, gConfig.u16HueGroundActive, 255, gConfig.u8LedGroundBrightness);
for (uint8_t i = 0; i < LED_NUM_GROUND; 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)) {
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aGroundActive[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aGroundActive[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aGroundActive[2];
} else {
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aGround[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aGround[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aGround[2];
}
} else if (nCell % 4 == 3) {
// This is a separating divider. Light it with the active colour
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aGroundActive[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aGroundActive[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aGroundActive[2];
} else {
// This is a non-separating divider. Light it based on the two cells either side
if (gu16PSoCDigital & (1 << nCell) && gu16PSoCDigital & (1 << (nCell + 1))) {
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aGroundActive[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aGroundActive[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aGroundActive[2];
} else {
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aGround[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aGround[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aGround[2];
}
}
}
}
void LED_WriteRGB(void) {
gu8LEDTx[0] = LED_CMD_RGB_FULL;
#ifdef LED_FIRMWARE_CFW
// "CFW" added this byte
gu8LEDTx[1] = su8LedSpecial | su8LedOff;
#endif
// Even when grounds are disabled, internal control overrides that
// If we're internally controlled, data will be HSV
if (gbLedDataIsControlledInt) {
PIN_LED_GROUND_PWR = 1;
pTxHSV->u8Cmd = LED_CMD_CUSTOM_HSV;
pTxHSV->u8GroundBrightness = _LED_GroundBrightness();
pTxHSV->u8WingBrightness = _LED_WingBrightness();
_LED_SetPower();
uint8_t u8aRGB[3];
// Convert from HSV to GRB
for (uint8_t i = 0; i < LED_NUM_GROUND; i++) {
HsvToRgb(u8aRGB, gaControlledIntLedData[LED_NUM_GROUND - i - 1].u16H,
gaControlledIntLedData[LED_NUM_GROUND - i - 1].u8S,
gaControlledIntLedData[LED_NUM_GROUND - i - 1].u8V);
LED_Ground_Internal_HSV(pTxHSV->aGround);
LED_Wings_Reactive_HSV(&pTxHSV->Wings);
} else if (gbLedDataIsControlledExt) {
// RGB control from the game
pTxRGB->u8Cmd = LED_CMD_CUSTOM_RGB;
pTxRGB->u8GroundBrightness = _LED_GroundBrightness();
pTxRGB->u8WingBrightness = _LED_WingBrightness();
_LED_SetPower();
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRGB[1];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRGB[0];
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRGB[2];
}
} else if (gConfig.u8LedGroundBrightness) {
PIN_LED_GROUND_PWR = 1;
LED_Ground_Controlled_RGB(pTxRGB->aGround);
LED_Wings_Controlled_RGB(&pTxRGB->Wings);
} else {
// User-set coloring scheme
pTxHSV->u8Cmd = LED_CMD_CUSTOM_HSV;
pTxHSV->u8GroundBrightness = _LED_GroundBrightness();
pTxHSV->u8WingBrightness = _LED_WingBrightness();
_LED_SetPower();
if (gbLedDataIsControlledExt) {
// Swap from BRG to GRB
for (uint8_t i = 0; i < LED_NUM_GROUND; i++) {
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] =
LED_ScaleU8(gu8aControlledExtLedData[i * 3 + 2], gConfig.u8LedGroundBrightness);
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] =
LED_ScaleU8(gu8aControlledExtLedData[i * 3 + 1], gConfig.u8LedGroundBrightness);
gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] =
LED_ScaleU8(gu8aControlledExtLedData[i * 3 + 0], gConfig.u8LedGroundBrightness);
}
} else if (gConfig.bEnableRainbow) {
LED_GroundRainbow();
if (gConfig.bEnableRainbow) {
LED_Ground_Rainbow_HSV(pTxHSV->aGround);
} else {
LED_GroundStatic();
LED_Ground_Static_HSV(pTxHSV->aGround);
}
} else {
PIN_LED_GROUND_PWR = 0;
LED_OffGround();
}
if (gConfig.u8LedWingBrightness) {
PIN_LED_WING_PWR = 1;
// TODO: Get data from game when gbLedDataIsControlledExt (HID, probably)
LED_AirWings();
} else {
PIN_LED_WING_PWR = 0;
LED_OffWings();
LED_Wings_Reactive_HSV(&pTxHSV->Wings);
}
}
#define I2C_WaitTimeout (SystemCoreClock / 5);
#define WAIT_INDEX(cond) \
do { \
u32TimeOutCnt = I2C_WaitTimeout; \
while (u16I2CRxIndex cond) { \
if (--u32TimeOutCnt == 0) return 0; \
} \
} while (0)
uint8_t LED_FMC_Read(uint32_t u32Offset, uint32_t* pu32Data) {
static volatile uint32_t u32Data;
u32Data = 0xFFFFFFFF;
uint32_t u32TimeOutCnt;
// Wait for anything in the buffer to be read
WAIT_INDEX(!= 0);
WAIT_INDEX(== 0);
// Request a 4-byte read from the LED board
gpu8I2CRx = (volatile uint8_t*)&u32Data;
gu8LEDTx[0] = 0;
gu8LEDTx[1] = u32Offset & 0xff;
gu8LEDTx[2] = (u32Offset >> 8) & 0xff;
gu8LEDTx[3] = (u32Offset >> 16) & 0xff;
gu8LEDTx[4] = (u32Offset >> 24) & 0xff;
gu8LEDTx[0] = LED_CMD_FMC_READ;
// Wait for our packet to be sent
WAIT_INDEX(!= 0);
WAIT_INDEX(== 0);
// Wait for the LED board to send its response
WAIT_INDEX(!= 5);
if (pu32Data) *pu32Data = u32Data;
return 1;
}

102
src/led.h
View File

@ -1,73 +1,49 @@
#pragma once
#include <stdint.h>
#include "led_shared.h"
#define LED_FIRMWARE_CFW
#ifndef LED_FIRMWARE_CFW
#define LED_DATA_OFFSET 1
#else
#define LED_DATA_OFFSET 2
#endif
// Defined as volatile due to the I2C interrupt reading at random times!
extern volatile uint8_t gu8LEDTx[LED_PACKET_MAX_SIZE];
// LED count definitions
#define LED_NUM_GROUND 31
#define LED_NUM_LEFT 24
#define LED_NUM_RIGHT 24
// HSV definitions
#define LED_HUE_SCALE 5 // For transmission to LED board in basic mode
#define LED_HUE_MAX 360
// Host->LED MCU commands
#define LED_CMD_BASIC 0xA5
#define LED_CMD_RGB_FULL 0x5A
// LED index names
#define LED_CELL_0 0
#define LED_CELL_1 2
#define LED_CELL_2 4
#define LED_CELL_3 6
#define LED_CELL_4 8
#define LED_CELL_5 10
#define LED_CELL_6 12
#define LED_CELL_7 14
#define LED_CELL_8 16
#define LED_CELL_9 18
#define LED_CELL_10 20
#define LED_CELL_11 22
#define LED_CELL_12 24
#define LED_CELL_13 26
#define LED_CELL_14 28
#define LED_CELL_15 30
#define LED_DIVIDER_0_1 1
#define LED_DIVIDER_1_2 3
#define LED_DIVIDER_2_3 5
#define LED_DIVIDER_3_4 7
#define LED_DIVIDER_4_5 9
#define LED_DIVIDER_5_6 11
#define LED_DIVIDER_6_7 13
#define LED_DIVIDER_7_8 15
#define LED_DIVIDER_8_9 17
#define LED_DIVIDER_9_10 19
#define LED_DIVIDER_10_11 21
#define LED_DIVIDER_11_12 23
#define LED_DIVIDER_12_13 25
#define LED_DIVIDER_13_14 27
#define LED_DIVIDER_14_15 29
typedef struct {
uint16_t u16H;
uint8_t u8S;
uint8_t u8V;
} hsv_t;
// Internal LED control, from the settings UI (gets priority)
extern hsv_t gaControlledIntLedData[LED_NUM_GROUND];
extern hsv_t gaControlledIntLedData[LED_NUM_GROUND_LOGICAL];
extern uint8_t gbLedDataIsControlledInt;
// External LED control, from the game
extern uint8_t gu8aControlledExtLedData[32 * 3];
extern rgb_t gaControlledExtLedData[32];
extern uint8_t gbLedDataIsControlledExt;
// For reception of data from I2C
extern volatile uint8_t* gpu8I2CRx;
extern volatile uint16_t u16I2CRxIndex;
extern uint8_t gbLedIsCustom;
// === Assigners (led_impl.c) ===
void LED_Wings_Reactive_HSV(_led_wings_hsv* pWings);
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_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_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_Ground_Controlled_RGB(rgb_t* aGround);
// === Exported functions ===
void HsvToHost(rgb_t* pRGB, uint16_t u16H, uint8_t u8S, uint8_t u8V);
void LED_I2C1_Init(void);
void LED_WriteBasicGrounds(void);
void LED_WriteRGB(void);
/**
* Write data out to the LEDs
*/
void LED_Write(void);
uint8_t LED_FMC_Read(uint32_t u32Offset, uint32_t* pu32Data);

273
src/led_impl.c Normal file
View File

@ -0,0 +1,273 @@
#include "tasoller.h"
static inline uint8_t LED_ScaleU8(uint8_t u8V, uint8_t u8Scale) {
// When using our custom firmware, don't perform scaling here because it'll be performed on the
// LED processor for us instead
#if LED_FIRMWARE_TYPE == LED_FW_OURS
return u8V;
#else
return ((uint16_t)u8V * (uint16_t)u8Scale) / 255;
#endif
}
static const uint8_t su8aWingSensors[LED_NUM_WING] = {
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, //
DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, //
DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, //
DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, //
};
#define SATURATION_ACTIVE 255
#define SATURATION_INACTIVE 240
#define VALUE_ACTIVE (gConfig.u8LedWingBrightness)
#define VALUE_INACTIVE (gConfig.u8LedWingBrightness / 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;
} else {
pWings->aWingL[i].h = gConfig.u16HueWingLeft;
pWings->aWingL[i].s = SATURATION_INACTIVE;
pWings->aWingL[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;
} else {
pWings->aWingR[i].h = gConfig.u16HueWingRight;
pWings->aWingR[i].s = SATURATION_INACTIVE;
pWings->aWingR[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 uint8_t u8Ticker = 0;
if (++u8Ticker == 5) {
u8Ticker = 0;
u16Hue++;
if (u16Hue == LED_HUE_MAX) u16Hue = 0;
}
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;
}
} else if (nCell % 4 == 3) {
h = LED_HUE_MAX / 2;
}
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;
}
}
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)) {
aGround[i].h = gConfig.u16HueGroundActive;
aGround[i].s = 255;
aGround[i].v = 255;
} else {
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))) {
aGround[i].h = gConfig.u16HueGroundActive;
aGround[i].s = 255;
aGround[i].v = 255;
} else {
aGround[i].h = gConfig.u16HueGround;
aGround[i].s = 255;
aGround[i].v = 255;
}
}
}
}
void LED_Ground_Internal_HSV(hsv_t* aGround) {
for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) {
aGround[i].h = gaControlledIntLedData[LED_NUM_GROUND_LOGICAL - i - 1].h;
aGround[i].s = gaControlledIntLedData[LED_NUM_GROUND_LOGICAL - i - 1].s;
aGround[i].v = gaControlledIntLedData[LED_NUM_GROUND_LOGICAL - i - 1].v;
}
}
void HsvToHost(rgb_t* pRGB, uint16_t u16H, uint8_t u8S, uint8_t u8V) {
if (u8S == 0) {
pRGB->host.r = u8V;
pRGB->host.g = u8V;
pRGB->host.b = u8V;
return;
}
uint8_t region = u16H / (LED_HUE_MAX / 6);
uint8_t remainder = (u16H - (region * (LED_HUE_MAX / 6))) * (255 / (LED_HUE_MAX / 6));
uint8_t p = (u8V * (255 - u8S)) >> 8;
uint8_t q = (u8V * (255 - ((u8S * remainder) >> 8))) >> 8;
uint8_t t = (u8V * (255 - ((u8S * (255 - remainder)) >> 8))) >> 8;
switch (region) {
case 0:
pRGB->host.r = u8V;
pRGB->host.g = t;
pRGB->host.b = p;
break;
case 1:
pRGB->host.r = q;
pRGB->host.g = u8V;
pRGB->host.b = p;
break;
case 2:
pRGB->host.r = p;
pRGB->host.g = u8V;
pRGB->host.b = t;
break;
case 3:
pRGB->host.r = p;
pRGB->host.g = q;
pRGB->host.b = u8V;
break;
case 4:
pRGB->host.r = t;
pRGB->host.g = p;
pRGB->host.b = u8V;
break;
default:
pRGB->host.r = u8V;
pRGB->host.g = p;
pRGB->host.b = q;
break;
}
return;
}
void LED_Wings_Reactive_RGB(_led_wings_rgb* pWings) {
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++) {
// 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;
} 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;
}
}
// 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++) {
// 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;
} 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;
}
}
}
void LED_Ground_Rainbow_RGB(rgb_t* aGround) {
// TODO: Even bother?
return;
}
void LED_Ground_Static_RGB(rgb_t* aGround) {
// TODO: Even bother?
rgb_t rgbGround;
rgb_t rgbGroundActive;
HsvToHost(&rgbGround, gConfig.u16HueGround, 255, gConfig.u8LedGroundBrightness);
HsvToHost(&rgbGroundActive, gConfig.u16HueGroundActive, 255, gConfig.u8LedGroundBrightness);
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)) {
aGround[i].host.r = rgbGroundActive.host.r;
aGround[i].host.g = rgbGroundActive.host.g;
aGround[i].host.b = rgbGroundActive.host.b;
} else {
aGround[i].host.r = rgbGround.host.r;
aGround[i].host.g = rgbGround.host.g;
aGround[i].host.b = rgbGround.host.b;
}
} else if (nCell % 4 == 3) {
// This is a separating divider. Light it with the active colour
aGround[i].host.r = rgbGroundActive.host.r;
aGround[i].host.g = rgbGroundActive.host.g;
aGround[i].host.b = rgbGroundActive.host.b;
} else {
// This is a non-separating divider. Light it based on the two cells either side
if (gu16PSoCDigital & (1 << nCell) && gu16PSoCDigital & (1 << (nCell + 1))) {
aGround[i].host.r = rgbGroundActive.host.r;
aGround[i].host.g = rgbGroundActive.host.g;
aGround[i].host.b = rgbGroundActive.host.b;
} else {
aGround[i].host.r = rgbGround.host.r;
aGround[i].host.g = rgbGround.host.g;
aGround[i].host.b = rgbGround.host.b;
}
}
}
}
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_Ground_Controlled_RGB(rgb_t* aGround) {
// Swap from BRG(game) to GRB(host)
for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) {
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);
}
}

213
src/led_shared.h Normal file
View File

@ -0,0 +1,213 @@
/**
* !!WARNING!!
* This file is included into the LED APROM firmware too.
* Don't place anything in here that would be inappropriate there.
* !!WARNING!!
*/
#pragma once
#include <stdint.h>
#include "_compiler.h"
// === LED Firmware Type Configuration ===
// (Only applicable on the host)
#define LED_FW_STOCK 0
#define LED_FW_MAINLAND 1
#define LED_FW_OURS 2
// #define LED_FIRMWARE_TYPE LED_FW_STOCK
// #define LED_FIRMWARE_TYPE LED_FW_MAINLAND
#define LED_FIRMWARE_TYPE LED_FW_OURS
// === LED Count Definitions ===
/**
* There are 48 LEDs for ground, but we only send 31 values
* They're mapped to the LEDs as follows:
* 00a11b22c33d44f55g66h77i...
*
* That is, we can't split-colour a key :P
*/
#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
// === Colour Byte Order Definitions ===
// What we know and love
#define RGB_R 0
#define RGB_G 1
#define RGB_B 2
// What WS2813 uses, and what we receive from the host in stock mode
#define GRB_R 1
#define GRB_G 0
#define GRB_B 2
// Our internal reversed WS2813 format
#define BRG_R 1
#define BRG_G 2
#define BRG_B 0
#define HOST_FORMAT GRB
#define _CHANNEL(fmt, c) fmt##_##c
#define CHANNEL(fmt, c) _CHANNEL(fmt, c)
#define HOST_R CHANNEL(HOST_FORMAT, R)
#define HOST_G CHANNEL(HOST_FORMAT, G)
#define HOST_B CHANNEL(HOST_FORMAT, B)
typedef union {
struct __packed {
uint8_t r;
uint8_t g;
uint8_t b;
} rgb;
// Data received from the game is in BRG format
struct __packed {
uint8_t b;
uint8_t r;
uint8_t g;
} game;
// WS2813 uses GRB, and that's what we received from the host
struct __packed {
uint8_t g;
uint8_t r;
uint8_t b;
} host;
// Internally we use a reversed WS2813 format (BRG)
struct __packed {
uint8_t b;
uint8_t r;
uint8_t g;
} internal;
uint8_t aRaw[3];
} rgb_t;
typedef struct __packed {
uint16_t h;
uint8_t s;
uint8_t v;
} hsv_t;
// === Host->LED MCU Commands ===
#define LED_I2C_REG_PACKET 0xFF
#define LED_CMD_BASIC 0xA5
#define LED_CMD_RGB_FULL 0x5A
#define LED_CMD_CUSTOM_RGB 0x5B
#define LED_CMD_CUSTOM_HSV 0x5C
#define LED_CMD_CUSTOM_MIXED 0x5D
#define LED_CMD_FMC_READ 0x5E
#define LED_CMD_FMC_ENTER_LDROM 0x5F
// === HOST->LED MCU Packets ===
typedef struct __packed {
rgb_t aWingL[LED_NUM_WING];
rgb_t aWingR[LED_NUM_WING];
} _led_wings_rgb;
typedef struct __packed {
hsv_t aWingL[LED_NUM_WING];
hsv_t aWingR[LED_NUM_WING];
} _led_wings_hsv;
typedef struct __packed {
uint8_t u8Cmd;
uint32_t u32Ground;
uint8_t u8WingFill;
#if LED_FIRMWARE_TYPE == LED_FW_MAINLAND
uint8_t u8Rainbow;
uint8_t u8Rsv07;
uint8_t u8Rsv08;
uint8_t u8Rsv09;
#else
uint8_t u8HueLeft;
uint8_t u8HueRight;
uint8_t u8HueGround;
uint8_t u8HueGroundActive;
#endif
uint8_t u8Flags0;
uint8_t u8Flags1;
} led_rx_basic, *Pled_rx_basic;
typedef struct __packed {
uint8_t u8Cmd;
#if LED_FIRMWARE_TYPE == LED_FW_MAINLAND
uint8_t u8Config;
#endif
rgb_t aGround[LED_NUM_GROUND_LOGICAL];
_led_wings_rgb Wings;
} led_rx_full, *Pled_rx_full;
typedef struct __packed {
uint8_t u8Cmd;
uint8_t u8GroundBrightness;
uint8_t u8WingBrightness;
rgb_t aGround[LED_NUM_GROUND_LOGICAL];
_led_wings_rgb Wings;
} led_rx_custom_rgb, *Pled_rx_custom_rgb;
typedef struct __packed {
uint8_t u8Cmd;
uint8_t u8GroundBrightness;
uint8_t u8WingBrightness;
rgb_t aGround[LED_NUM_GROUND_LOGICAL];
_led_wings_hsv Wings;
} led_rx_custom_mixed, *Pled_rx_custom_mixed;
typedef struct __packed {
uint8_t u8Cmd;
uint8_t u8GroundBrightness;
uint8_t u8WingBrightness;
hsv_t aGround[LED_NUM_GROUND_LOGICAL];
_led_wings_hsv Wings;
} led_rx_custom_hsv, *Pled_rx_custom_hsv;
typedef struct __packed {
uint8_t u8Cmd;
uint32_t u32Offset;
} led_rx_fmc_read, *Pled_rx_fmc_read;
#define LED_PACKET_MAX_SIZE \
Maximum(Maximum(Maximum(sizeof(led_rx_basic), sizeof(led_rx_full)), \
Maximum(sizeof(led_rx_custom_rgb), sizeof(led_rx_custom_hsv))), \
sizeof(led_rx_custom_mixed))
// === LED index names ===
#define LED_CELL_0 0
#define LED_CELL_1 2
#define LED_CELL_2 4
#define LED_CELL_3 6
#define LED_CELL_4 8
#define LED_CELL_5 10
#define LED_CELL_6 12
#define LED_CELL_7 14
#define LED_CELL_8 16
#define LED_CELL_9 18
#define LED_CELL_10 20
#define LED_CELL_11 22
#define LED_CELL_12 24
#define LED_CELL_13 26
#define LED_CELL_14 28
#define LED_CELL_15 30
#define LED_DIVIDER_0_1 1
#define LED_DIVIDER_1_2 3
#define LED_DIVIDER_2_3 5
#define LED_DIVIDER_3_4 7
#define LED_DIVIDER_4_5 9
#define LED_DIVIDER_5_6 11
#define LED_DIVIDER_6_7 13
#define LED_DIVIDER_7_8 15
#define LED_DIVIDER_8_9 17
#define LED_DIVIDER_9_10 19
#define LED_DIVIDER_10_11 21
#define LED_DIVIDER_11_12 23
#define LED_DIVIDER_12_13 25
#define LED_DIVIDER_13_14 27
#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

View File

@ -2,69 +2,70 @@
uint8_t volatile gu8HIDIO4Ready = 0;
uint8_t volatile gu8HIDMiscReady = 0;
uint32_t gu32NowMs = 0;
#define DEBOUNCE_COUNT 8
#define NUM_BUTTONS 8
#define DEBOUNCE_ON (2 * 4) // We need a digital high for 2ms to latch on
#define DEBOUNCE_OFF (8 * 4) // Then a digital low for 8ms to latch off
uint8_t TickDebouncer(uint8_t u8Buttons) {
static uint8_t u8Counters[8] = { 0 };
uint8_t debounced = 0;
for (uint8_t i = 0; i < 8; i++) {
if (u8Buttons & (1 << i))
u8Counters[i] = 0;
else if (u8Counters[i] < DEBOUNCE_COUNT)
u8Counters[i]++;
static uint8_t u8CountUp[NUM_BUTTONS] = { 0 };
static uint8_t u8CountDown[NUM_BUTTONS] = { 0 };
if (u8Counters[i] < DEBOUNCE_COUNT) debounced |= (1 << i);
uint8_t debounced = 0;
for (uint8_t i = 0; i < NUM_BUTTONS; i++) {
if (u8Buttons & (1 << i)) {
if (u8CountUp[i] < DEBOUNCE_ON)
u8CountUp[i]++;
else
u8CountDown[i] = DEBOUNCE_OFF;
} else {
if (u8CountDown[i])
u8CountDown[i]--;
else
u8CountUp[i] = 0;
}
if (u8CountDown[i]) debounced |= (1 << i);
}
return debounced;
}
/**
* @brief Perform a sensor read using Quasi-bidirectional Mode
*
* See TRM section 6.5.5.4 for details
*/
static inline uint8_t DigitalRead(volatile uint32_t* pData, GPIO_T* pGpio, uint32_t u32Pin) {
GPIO_SetMode(pGpio, u32Pin, GPIO_PMD_INPUT);
*pData = 1;
DELAY_CYCLES_2;
DELAY_CYCLES_2;
DELAY_CYCLES_2;
GPIO_SetMode(pGpio, u32Pin, GPIO_PMD_QUASI);
DELAY_CYCLES_2;
DELAY_CYCLES_2;
DELAY_CYCLES_2;
return *pData;
}
uint8_t gu8DigitalButtons;
#define DELAY_FN 2
#define DELAY_AIR 2
void Digital_TickInputs() {
uint8_t u8Buttons = 0;
GPIO_SetMode(_PIN_FN1, GPIO_PMD_INPUT);
DelayCycles_Small(DELAY_FN);
GPIO_SetMode(_PIN_FN1, GPIO_PMD_QUASI);
u8Buttons |= PIN_FN1 ? 0 : DIGITAL_FN1_Msk;
// The two FN buttons need a quasi read cycle
u8Buttons |= DigitalRead(&PIN_FN1, _PIN_FN1) ? 0 : DIGITAL_FN2_Msk;
u8Buttons |= DigitalRead(&PIN_FN2, _PIN_FN1) ? 0 : DIGITAL_FN1_Msk;
GPIO_SetMode(_PIN_FN2, GPIO_PMD_INPUT);
DelayCycles_Small(DELAY_FN);
GPIO_SetMode(_PIN_FN2, GPIO_PMD_QUASI);
u8Buttons |= PIN_FN2 ? 0 : DIGITAL_FN2_Msk;
// The 6 AIR sensors drive a ~4.5V signal on the pin, so are just in INPUT mode
u8Buttons |= DigitalRead(&PIN_AIR1, _PIN_AIR1) ? DIGITAL_AIR1_Msk : 0;
u8Buttons |= DigitalRead(&PIN_AIR2, _PIN_AIR2) ? DIGITAL_AIR2_Msk : 0;
u8Buttons |= DigitalRead(&PIN_AIR3, _PIN_AIR3) ? DIGITAL_AIR3_Msk : 0;
u8Buttons |= DigitalRead(&PIN_AIR4, _PIN_AIR4) ? DIGITAL_AIR4_Msk : 0;
u8Buttons |= DigitalRead(&PIN_AIR5, _PIN_AIR5) ? DIGITAL_AIR5_Msk : 0;
u8Buttons |= DigitalRead(&PIN_AIR6, _PIN_AIR6) ? DIGITAL_AIR6_Msk : 0;
GPIO_SetMode(_PIN_AIR1, GPIO_PMD_INPUT);
DelayCycles_Small(DELAY_AIR);
GPIO_SetMode(_PIN_AIR1, GPIO_PMD_QUASI);
u8Buttons |= PIN_AIR1 ? DIGITAL_AIR1_Msk : 0;
GPIO_SetMode(_PIN_AIR2, GPIO_PMD_INPUT);
DelayCycles_Small(DELAY_AIR);
GPIO_SetMode(_PIN_AIR2, GPIO_PMD_QUASI);
u8Buttons |= PIN_AIR2 ? DIGITAL_AIR2_Msk : 0;
GPIO_SetMode(_PIN_AIR3, GPIO_PMD_INPUT);
DelayCycles_Small(DELAY_AIR);
GPIO_SetMode(_PIN_AIR3, GPIO_PMD_QUASI);
u8Buttons |= PIN_AIR3 ? DIGITAL_AIR3_Msk : 0;
GPIO_SetMode(_PIN_AIR4, GPIO_PMD_INPUT);
DelayCycles_Small(DELAY_AIR);
GPIO_SetMode(_PIN_AIR4, GPIO_PMD_QUASI);
u8Buttons |= PIN_AIR4 ? DIGITAL_AIR4_Msk : 0;
GPIO_SetMode(_PIN_AIR5, GPIO_PMD_INPUT);
DelayCycles_Small(DELAY_AIR);
GPIO_SetMode(_PIN_AIR5, GPIO_PMD_QUASI);
u8Buttons |= PIN_AIR5 ? DIGITAL_AIR5_Msk : 0;
GPIO_SetMode(_PIN_AIR6, GPIO_PMD_INPUT);
DelayCycles_Small(DELAY_AIR);
GPIO_SetMode(_PIN_AIR6, GPIO_PMD_QUASI);
u8Buttons |= PIN_AIR6 ? DIGITAL_AIR6_Msk : 0;
gu8DigitalButtons = TickDebouncer(u8Buttons);
u8Buttons = TickDebouncer(u8Buttons);
gu8DigitalButtons = u8Buttons;
}
int _entry(void) {
@ -78,7 +79,7 @@ int _entry(void) {
FMC_EEPROM_Load();
gu8VcomReady = 1;
gu8VComReady = 1;
gu8HIDIO4Ready = 1;
gu8HIDMiscReady = 1;
@ -88,20 +89,30 @@ int _entry(void) {
* delay added first.
* To make matters worse, if we ever actually call PSoCSetFingerCapacitance it ends up with
* huge asymmetry between the two PSoCs, which is just unworkable.
*
* Instead, see the non-blocking logic in the 1ms tick block.
*/
// uint16_t u16InitialFingerCap = PSoCGetFingerCapacitance();
// if (u16InitialFingerCap != gu16PSoCFingerCap) {
// PSoCSetFingerCapacitance(gu16PSoCFingerCap);
// PSoCSetFingerCapacitance(gu16PSoCFingerCap, 1);
// }
static uint32_t su32NowMs = 0;
su32NowMs++;
gbLedIsCustom = LED_FMC_Read(0, NULL);
gu32NowMs = 0;
uint8_t bPSoCHasTalked = 0;
while (1) {
USB_VCOM_Tick();
if (bPSoCDirty) {
bPSoCDirty = 0;
if (bPSoCDirtyVolatile) {
bPSoCDirtyVolatile = 0;
PSoC_PostProcessing();
if (!bPSoCHasTalked) {
PSoC_SetFingerCapacitanceFromConfig(1);
bPSoCHasTalked = 1;
}
}
Slider_TickSerial();
@ -114,24 +125,17 @@ int _entry(void) {
}
if (gu8Do1msTick) {
su32NowMs++;
// Update timer
gu32NowMs++;
gu8Do1msTick = 0;
Slider_Tick1ms();
USB_VCOM_Tick();
PSoC_DigitalCalc();
UI_Tick();
// LED_WriteBasicGrounds();
LED_WriteRGB();
} /* else {
DelayCycles(45);
}*/
// Wait until we know both PSoC are awake and talking before we attempt configure them
if (gu8PSoCSeenData == 0b011) {
PSoC_SetFingerCapacitanceFromConfig();
gu8PSoCSeenData |= 0b100;
LED_Write();
}
FMC_EEPROM_Store();

View File

@ -46,16 +46,16 @@
#define USB_MUX_HOST 0
#define USB_MUX_LEDS 1
#define DIGITAL_FN1_Pos 0
#define DIGITAL_FN2_Pos 1
#define DIGITAL_FN2_Pos 0
#define DIGITAL_FN1_Pos 1
#define DIGITAL_AIR1_Pos 2
#define DIGITAL_AIR2_Pos 3
#define DIGITAL_AIR3_Pos 4
#define DIGITAL_AIR4_Pos 5
#define DIGITAL_AIR5_Pos 6
#define DIGITAL_AIR6_Pos 7
#define DIGITAL_FN1_Msk (1 << DIGITAL_FN1_Pos)
#define DIGITAL_FN2_Msk (1 << DIGITAL_FN2_Pos)
#define DIGITAL_FN1_Msk (1 << DIGITAL_FN1_Pos)
#define DIGITAL_AIR1_Msk (1 << DIGITAL_AIR1_Pos)
#define DIGITAL_AIR2_Msk (1 << DIGITAL_AIR2_Pos)
#define DIGITAL_AIR3_Msk (1 << DIGITAL_AIR3_Pos)

View File

@ -1,6 +1,18 @@
#include "tasoller.h"
// For some reason, any level of optimisation
// #pragma GCC diagnostic push
// #pragma GCC diagnostic warning "-Wpedantic"
// #pragma GCC push_options
// #pragma GCC optimize ("O0")
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 :)
uint16_t gu16PSoCThreshold[32] = {
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
};
uint32_t gu32PSoCDigital = 0;
uint32_t gu32PSoCDigitalPos = 0;
@ -11,13 +23,16 @@ uint16_t gu16PSoCDigitalPos = 0;
uint16_t gu16PSoCDigitalNeg = 0;
uint16_t gu16PSoCDigitalTrig = 0;
uint8_t gu8PSoCSeenData = 0;
uint32_t gu32LastCapSenseStart = 0;
uint32_t gu32LastCapSenseEnd = 0;
volatile uint8_t bPSoCDirty = 0;
volatile uint8_t bPSoCDirtyVolatile = 0;
volatile uint8_t bForceSliderSend = 0;
static void* pu8PsocRxDestination = NULL;
static uint8_t pu8PsocRxDestinationLen = 0;
static volatile uint8_t* pu8PsocGotData = NULL;
static volatile uint8_t* volatile pu8PsocGotData = NULL;
static volatile PSoC_CMD_RX eBlockingCommand = _PSoC_CMD_RX_NONE;
static void PSoC_HandleRx(PSoC_CMD_RX eCmd, uint8_t u8Len, uint8_t* u8Data) {
if (eCmd != PSoC_CMD_RX_SLAVE_DIFF && eCmd != PSoC_CMD_RX_MASTER_DIFF &&
eCmd != PSoC_CMD_RX_CS_START) {
@ -25,40 +40,76 @@ static void PSoC_HandleRx(PSoC_CMD_RX eCmd, uint8_t u8Len, uint8_t* u8Data) {
debug++;
}
uint8_t u8PsocDataOffset = 0;
switch (eCmd) {
case PSoC_CMD_RX_REQUEST_FINGER_CAP:
PSoC_SetFingerCapacitanceFromConfig();
// We're in the IRQ here, so don't block on this!
PSoC_SetFingerCapacitanceFromConfig(0);
return;
// We don't care about these
// Debug traces
case PSoC_CMD_RX_CS_START:
gu32LastCapSenseStart = gu32NowMs;
return;
case PSoC_CMD_RX_CS_END:
gu32LastCapSenseEnd = gu32NowMs;
return;
case PSoC_CMD_RX_SLAVE_DIFF:
u8PsocDataOffset = 16;
gu8PSoCSeenData |= 1;
// Falls through
case PSoC_CMD_RX_MASTER_DIFF:
if (eCmd == PSoC_CMD_RX_MASTER_DIFF) gu8PSoCSeenData |= 2;
// Request a refresh of our touch thresholds
PSoC_GetDebug(PSoC_CMD_DEBUG_SLAVE_TOUCH_TH, NULL);
if (u8Len != 32) return;
for (uint8_t i = 0; i < 16; i++) {
gu16PSoCDiff[u8PsocDataOffset + i] = u8Data[(i * 2)] << 8;
gu16PSoCDiff[u8PsocDataOffset + i] |= u8Data[(i * 2) + 1];
gu16PSoCDiff[16 + i] = u8Data[(i * 2)] << 8;
gu16PSoCDiff[16 + i] |= u8Data[(i * 2) + 1];
}
bPSoCDirty = 1;
bPSoCDirtyVolatile = 1;
return;
case PSoC_CMD_RX_MASTER_DIFF:
// Request a refresh of our touch thresholds
PSoC_GetDebug(PSoC_CMD_DEBUG_MASTER_TOUCH_TH, NULL);
if (u8Len != 32) return;
for (uint8_t i = 0; i < 16; i++) {
gu16PSoCDiff[i] = u8Data[(i * 2)] << 8;
gu16PSoCDiff[i] |= u8Data[(i * 2) + 1];
}
bPSoCDirtyVolatile = 1;
return;
// Arbitrary data reception
case PSoC_CMD_RX_MASTER_TOUCH_TH:
if (u8Len == 32) {
for (uint8_t i = 0; i < 16; i++) {
gu16PSoCThreshold[i] = u8Data[(i * 2)] << 8;
gu16PSoCThreshold[i] |= u8Data[(i * 2) + 1];
}
}
goto arbitrary_data_reception;
case PSoC_CMD_RX_SLAVE_TOUCH_TH:
if (u8Len == 32) {
for (uint8_t i = 0; i < 16; i++) {
gu16PSoCThreshold[16 + i] = u8Data[(i * 2)] << 8;
gu16PSoCThreshold[16 + i] |= u8Data[(i * 2) + 1];
}
}
goto arbitrary_data_reception;
case PSoC_CMD_RX_MASTER_FINGER_TH:
case PSoC_CMD_RX_MASTER_HYSTERESIS:
case PSoC_CMD_RX_SLAVE_FINGER_TH:
case PSoC_CMD_RX_SLAVE_HYSTERESIS:
case PSoC_CMD_RX_GET_FINGER_CAP:
case PSoC_CMD_RX_SET_FINGER_CAP:
case PSoC_CMD_RX_ENABLE_DEBUG:
arbitrary_data_reception:
// Only handle the command if it's what we were actually blocking on
if (eCmd != eBlockingCommand) {
// This was a rogue command! (breakpoint here for PSoC diagnosis stuff)
return;
}
if (pu8PsocRxDestination) {
if (u8Len > pu8PsocRxDestinationLen) u8Len = pu8PsocRxDestinationLen;
memcpy(pu8PsocRxDestination, u8Data, u8Len);
@ -71,8 +122,84 @@ static void PSoC_HandleRx(PSoC_CMD_RX eCmd, uint8_t u8Len, uint8_t* u8Data) {
pu8PsocGotData = NULL;
}
return;
// Nonsense, but just to satisfy the switch condition validation :)
case _PSoC_CMD_RX_NONE:
return;
}
}
/**
* @brief Returns 1 on successful reception of a command, 0 otherwise
*
* @param eCommand
* @return uint8_t
*/
static volatile uint8_t su8Ready = 0;
static uint8_t PSoC_Await_Command(PSoC_CMD_RX eCommand) {
su8Ready = 0;
pu8PsocGotData = &su8Ready;
// TODO: What timeout does this actually work out to be?
// With O0, it looks like /33 = 1 second (ie the loop is 33 clocks)
// Haven't tested Os which is what we'd want for prod
uint32_t u32Timeout = (SystemCoreClock / 33) / 16; // 62.5ms
eBlockingCommand = eCommand;
// Block on reception of the command
while (!su8Ready) {
// Timeout condition
if (--u32Timeout == 0) {
eBlockingCommand = _PSoC_CMD_RX_NONE;
return 0;
}
}
eBlockingCommand = _PSoC_CMD_RX_NONE;
return 1;
}
static uint8_t PSoC_Valid_Len(PSoC_CMD_RX eCmd, uint8_t u8Len) {
switch (eCmd) {
case PSoC_CMD_RX_CS_START:
case PSoC_CMD_RX_CS_END:
// We should never be seeing a length for these!
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:
case PSoC_CMD_RX_SLAVE_DIFF:
case PSoC_CMD_RX_MASTER_TOUCH_TH:
case PSoC_CMD_RX_SLAVE_TOUCH_TH:
case PSoC_CMD_RX_MASTER_FINGER_TH:
case PSoC_CMD_RX_MASTER_HYSTERESIS:
case PSoC_CMD_RX_SLAVE_FINGER_TH:
case PSoC_CMD_RX_SLAVE_HYSTERESIS:
case PSoC_CMD_RX_GET_FINGER_CAP:
return u8Len == 0x20;
// Nonsense
case _PSoC_CMD_RX_NONE:
return 0;
}
return 0;
}
static inline uint8_t PSoC_Validate_Sum(PSoC_CMD_RX eCmd, uint8_t u8Observed, uint8_t u8Received) {
switch (eCmd) {
// These four commands are broken, and their checksum is always zero
case PSoC_CMD_RX_MASTER_FINGER_TH:
case PSoC_CMD_RX_MASTER_HYSTERESIS:
case PSoC_CMD_RX_SLAVE_FINGER_TH:
case PSoC_CMD_RX_SLAVE_HYSTERESIS:
return u8Received == 0;
default:
return u8Received == u8Observed;
}
}
void UART1_IRQHandler(void) {
static uint8_t su8RxData[32] = { 0 };
static uint8_t su8State = 0;
@ -88,18 +215,15 @@ void UART1_IRQHandler(void) {
switch (su8State) {
case 0:
eCmd = u8Data; // For debugging
switch (u8Data) {
// Just go away
// Commands with no payload
case PSoC_CMD_RX_CS_START:
case PSoC_CMD_RX_CS_END:
return;
// Commands with no payload
case PSoC_CMD_RX_SET_FINGER_CAP:
PSoC_HandleRx(u8Data, 0, NULL);
// 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:
@ -114,6 +238,11 @@ void UART1_IRQHandler(void) {
su8Sum = u8Data;
su8State++;
return;
// Unknown command
default:
su8State = 0;
return;
}
return;
case 1:
@ -125,16 +254,28 @@ void UART1_IRQHandler(void) {
if (!su8Len) su8State++;
// Packets too large for reception need dropped
if (su8Len > sizeof su8RxData) su8State = 0;
if (!PSoC_Valid_Len(eCmd, su8Len)) {
// This isn't the right length for this command, so we know we're out of sync
su8State = 0;
}
return;
case 2:
su8Sum += u8Data;
su8RxData[su8Index++] = u8Data;
if (su8Index == su8Len) su8State++;
if (su8Index == su8Len) {
su8State++;
}
return;
case 3:
// Only process packets with a valid checksum
if (su8Sum == u8Data) PSoC_HandleRx(eCmd, su8Len, su8RxData);
su8State = 0;
if (PSoC_Validate_Sum(eCmd, su8Sum, u8Data)) {
PSoC_HandleRx(eCmd, su8Len, su8RxData);
su8State = 0;
} else {
// For breakpoints
su8State = 0;
}
return;
// If we somehow land in an invalid state (should be impossible), make sure we can recover
@ -144,29 +285,43 @@ void UART1_IRQHandler(void) {
}
}
static inline void PSoC_Cmd(PSoC_CMD_TX eCmd, uint8_t u8D0, uint8_t u8D1, uint8_t u8Blocking) {
static uint8_t su8Ready = 0;
if (u8Blocking) {
su8Ready = 0;
pu8PsocGotData = &su8Ready;
}
static inline void _UART_Write(uint8_t u8Data) {
while (UART_IS_TX_FULL(UART1))
;
UART_WRITE(UART1, u8Data);
}
static inline void PSoC_Cmd(PSoC_CMD_TX eCmd, uint8_t u8D0, uint8_t u8D1, PSoC_CMD_RX eBlocking) {
// The protocol for the PSoCs has no sync byte
// This means it's quite likely we're going to sometimes be parsing in the middle of a packets,
// causing us to potentially lose future packets that we wanted to receive.
//
// To avoid this situation, we're going to repeatedly send the packet on a timeout.
//
// If the PSoC is totally non-responsive this will deadlock. For now that's probably fine. We
// can add a retry counter down the line if it causes problems.
do {
_UART_Write(eCmd);
_UART_Write(2);
_UART_Write(u8D0);
_UART_Write(u8D1);
_UART_Write(eCmd + 2 + u8D0 + u8D1);
static uint8_t u8Packet[5];
u8Packet[0] = eCmd;
u8Packet[1] = 2;
u8Packet[2] = u8D0;
u8Packet[3] = u8D1;
u8Packet[4] = eCmd + 2 + u8D0 + u8D1;
for (uint8_t i = 0; i < 5; i++) {
while (UART_IS_TX_FULL(UART1))
;
UART_WRITE(UART1, u8Packet[i]);
}
if (eBlocking == _PSoC_CMD_RX_NONE) break;
if (PSoC_Await_Command(eBlocking)) break;
if (u8Blocking) {
while (!su8Ready)
;
}
// The issue with sync bytes goes both ways. Our packets are 5 bytes long, and the PSoC will
// error out if the second byte isn't a 2 (and stop reading), so by ensuring we send an
// odd-length stream we'll be back in sync eventually
//
// It can take quite a few failed packets to bring the PSoC back into sync. Once we have the
// PSoC in sync, it should stay in sync for the duration of the program execution. We
// perform a blocking sensitivity assignment at the start of the program, which is hopefully
// where this resync should ocur if required.
//
//
// ...that's all in theory though.
// In practice it seems if we ever break sync the PSoC will need a hard reset.
} while (1);
}
uint16_t PSoC_GetFingerCapacitance(void) {
@ -174,27 +329,50 @@ uint16_t PSoC_GetFingerCapacitance(void) {
pu8PsocRxDestination = &u16FingerCap;
pu8PsocRxDestinationLen = sizeof u16FingerCap;
PSoC_Cmd(PSoC_CMD_TX_GET_FINGER_CAP, 0, 0, 1);
PSoC_Cmd(PSoC_CMD_TX_GET_FINGER_CAP, 0, 0, PSoC_CMD_RX_GET_FINGER_CAP);
return u16FingerCap;
return BYTESWAP_U16(u16FingerCap);
}
void PSoC_GetDebug(PSoC_CMD_DEBUG u8Cmd, uint8_t* pu8Data, volatile uint8_t* pu8Ready) {
pu8PsocGotData = pu8Ready;
void PSoC_GetDebug(PSoC_CMD_DEBUG u8Cmd, uint8_t* pu8Data) {
pu8PsocRxDestination = pu8Data;
pu8PsocRxDestinationLen = 32;
PSoC_Cmd(PSoC_CMD_TX_GET_DEBUG, 0, u8Cmd, 0);
pu8PsocRxDestinationLen = pu8Data ? 32 : 0;
PSoC_CMD_RX eRet = _PSoC_CMD_RX_NONE;
if (pu8Data != NULL) {
switch (u8Cmd) {
case PSoC_CMD_DEBUG_MASTER_TOUCH_TH:
eRet = PSoC_CMD_RX_MASTER_TOUCH_TH;
break;
case PSoC_CMD_DEBUG_SLAVE_TOUCH_TH:
eRet = PSoC_CMD_RX_SLAVE_TOUCH_TH;
break;
case PSoC_CMD_DEBUG_MASTER_FINGER_TH:
eRet = PSoC_CMD_RX_MASTER_FINGER_TH;
break;
case PSoC_CMD_DEBUG_SLAVE_FINGER_TH:
eRet = PSoC_CMD_RX_SLAVE_FINGER_TH;
break;
case PSoC_CMD_DEBUG_MASTER_HYSTERESIS:
eRet = PSoC_CMD_RX_MASTER_HYSTERESIS;
break;
case PSoC_CMD_DEBUG_SLAVE_HYSTERESIS:
eRet = PSoC_CMD_RX_SLAVE_HYSTERESIS;
break;
}
}
PSoC_Cmd(PSoC_CMD_TX_GET_DEBUG, 0, u8Cmd, eRet);
}
void PSoC_SetFingerCapacitance(uint16_t u16FingerCap) {
void PSoC_SetFingerCapacitance(uint16_t u16FingerCap, uint8_t u8Blocking) {
if (u16FingerCap > 1023) u16FingerCap = 1023;
// PSoC_Cmd(PSoC_CMD_TX_SET_FINGER_CAP, u16FingerCap >> 8, u16FingerCap & 0xff, 1);
PSoC_Cmd(PSoC_CMD_TX_SET_FINGER_CAP, u16FingerCap >> 8, u16FingerCap & 0xff, 1);
PSoC_Cmd(PSoC_CMD_TX_SET_FINGER_CAP, u16FingerCap >> 8, u16FingerCap & 0xff,
u8Blocking ? PSoC_CMD_RX_SET_FINGER_CAP : _PSoC_CMD_RX_NONE);
}
void PSoC_SetFingerCapacitanceFromConfig(void) {
PSoC_SetFingerCapacitance(PSoC_FINGER_CAP_MIN + (16 - gConfig.u8Sens) * PSoC_FINGER_CAP_STEP);
void PSoC_SetFingerCapacitanceFromConfig(uint8_t u8Blocking) {
PSoC_SetFingerCapacitance(PSoC_FINGER_CAP_MIN + (16 - gConfig.u8Sens) * PSoC_FINGER_CAP_STEP,
u8Blocking);
}
void PSoC_EnableDebug(uint8_t u8D1, uint8_t u8D2) {
// TODO: Finish figuring out what values these two bytes need
PSoC_Cmd(PSoC_CMD_TX_ENABLE_DEBUG, u8D1, u8D2, 1);
void PSoC_SetDebug(PSoC_DebugFlag eFlag, uint8_t bEnable) {
PSoC_Cmd(PSoC_CMD_TX_ENABLE_DEBUG, eFlag, bEnable, PSoC_CMD_RX_ENABLE_DEBUG);
}
uint8_t gu8GroundData[32];
@ -205,20 +383,33 @@ void PSoC_PostProcessing(void) {
uint8_t j = (15 - (i / 2)) * 2 + (i & 1);
const uint16_t u16Pad = gu16PSoCDiff[i];
const uint16_t u16TouchTh = gu16PSoCThreshold[i];
const uint16_t u16Min = gConfig.u16PSoCScaleMin[i];
const uint16_t u16Max = gConfig.u16PSoCScaleMax[i];
if (u16Pad < u16Min) {
gu8GroundData[j] = 0;
} else if (u16Pad > u16Max) {
// Chunithm defaults to a threshold of 20
// As such, we need to scale u16Pad such that (0,u16TouchTh)=(0,20)
//
// This can go over 255, so make sure to clamp it
uint16_t u16Val = (u16Pad * 20) / u16TouchTh;
if (u16Val > 255)
gu8GroundData[j] = 255;
} else {
// Multiply by 255 first to retain precision
// This can overflow u16, hence the u32 cast pre-mult
gu8GroundData[j] = ((uint32_t)(u16Pad - u16Min) * 255) / (u16Max - u16Min);
}
else
gu8GroundData[j] = u16Val;
// const uint16_t u16Min = gConfig.u16PSoCScaleMin[i];
// const uint16_t u16Max = gConfig.u16PSoCScaleMax[i];
// if (u16Pad < u16Min) {
// gu8GroundData[j] = 0;
// } else if (u16Pad > u16Max) {
// gu8GroundData[j] = 255;
// } else {
// // Multiply by 255 first to retain precision
// // This can overflow u16, hence the u32 cast pre-mult
// gu8GroundData[j] = ((uint32_t)(u16Pad - u16Min) * 255) / (u16Max - u16Min);
// }
}
bForceSliderSend = 1;
}
/**
* @brief Calculate digital input data from PSoC values
@ -283,3 +474,6 @@ void PSoC_DigitalCalc(void) {
}
}
}
// #pragma GCC diagnostic pop
// #pragma GCC pop_options

View File

@ -5,22 +5,26 @@
#define PSoC_DATA_SIZE 32
#define PSoC_PACKET_SIZE (2 + PSoC_DATA_SIZE)
// Used for calculation of PSoC_FINGER_CAP_MIN
// The difference between CAP and MIN must be divisible by 8
#define PSoC_FINGER_CAP_MIN 20 // 0.2pF
#define PSoC_DEFAULT_CAP 340 // 3.2pF
// Maximum is CAP*2-MIN*2 ie 6.0pF
// The stupidly high gain kicks in around 0.7pF. It's not directly based on fingerCap though
// so 1.0pF gives us a bit of a margin to ensure we avoid it
#define PSoC_FINGER_CAP_MIN 100 // 1.0pF
#define PSoC_DEFAULT_CAP 500 // 5.0pF
// Maximum is CAP*2-MIN*2 ie 8.0pF
#define PSoC_FINGER_CAP_STEP ((PSoC_DEFAULT_CAP - PSoC_FINGER_CAP_MIN) / 8)
typedef enum {
typedef enum : uint8_t {
// Host -> PSoC
PSoC_CMD_TX_GET_FINGER_CAP = 0xB5,
PSoC_CMD_TX_GET_DEBUG = 0xB6,
PSoC_CMD_TX_SET_FINGER_CAP = 0xBD,
PSoC_CMD_TX_ENABLE_DEBUG = 0xDE,
} PSoC_CMD_TX;
typedef enum {
typedef enum : uint8_t {
// We'd use 0x00, but that's used, so 0x01 it is
_PSoC_CMD_RX_NONE = 0x01,
// PSoC -> Host
PSoC_CMD_RX_MASTER_DIFF = 0xAD,
PSoC_CMD_RX_SLAVE_DIFF = 0xAF,
@ -39,24 +43,28 @@ typedef enum {
// Packets that get a direct response
PSoC_CMD_RX_GET_FINGER_CAP = PSoC_CMD_TX_GET_FINGER_CAP,
PSoC_CMD_RX_SET_FINGER_CAP = PSoC_CMD_TX_SET_FINGER_CAP,
PSoC_CMD_RX_ENABLE_DEBUG = PSoC_CMD_TX_ENABLE_DEBUG,
} PSoC_CMD_RX;
typedef enum {
typedef enum : uint8_t {
PSoC_CMD_DEBUG_MASTER_TOUCH_TH = 0,
PSoC_CMD_DEBUG_SLAVE_TOUCH_TH,
PSoC_CMD_DEBUG_MASTER_FINGER_TH,
PSoC_CMD_DEBUG_SLAVE_FINGER_TH,
PSoC_CMD_DEBUG_SLAVE_FINGER_TH, // Broken!
PSoC_CMD_DEBUG_MASTER_HYSTERESIS,
PSoC_CMD_DEBUG_SLAVE_HYSTERESIS,
PSoC_CMD_DEBUG_SLAVE_HYSTERESIS, // Broken!
} PSoC_CMD_DEBUG;
// Difference between the raw count value and the baseline, from SmartSense
extern uint16_t gu16PSoCDiff[32];
// Has the difference data changed?
extern volatile uint8_t bPSoCDirty;
// Has the difference data changed in the interrupt handler?
extern volatile uint8_t bPSoCDirtyVolatile;
// Has the post-processed difference data changed?
extern volatile uint8_t bForceSliderSend;
// Used for producing gu32PSoCDigital and gu16PSoCDigital
#define PSoC_INTERNAL_DIGITAL_TH 0
// (Same threshold as default in Chunithm)
#define PSoC_INTERNAL_DIGITAL_TH 20
// PSoC data, re-interpreted as digital inputs
extern uint32_t gu32PSoCDigital;
@ -68,27 +76,16 @@ extern uint16_t gu16PSoCDigitalPos;
extern uint16_t gu16PSoCDigitalNeg;
extern uint16_t gu16PSoCDigitalTrig;
extern uint8_t gu8PSoCSeenData;
#define PSoC_RAW_T0 34
#define PSoC_RAW_T1 91
#define PSoC_RAW_T2 121
#define PSoC_RAW_MIN 199
#define PSoC_SCALE 5 // ie to scale from [0~4ff] to [0~ff]
#define PSoC_OUT_MIN 20
#define PSoC_OUT_MAX 255
#define PSoC_SCALE_MIN (0 + 128)
#define PSoC_SCALE_MAX (0x4ff - 128 - 256)
#define PSoC_SCALE_RANGE (3) // ie to scale from [min~max] to [0~ff]
//
extern uint32_t gu32LastCapSenseStart;
extern uint32_t gu32LastCapSenseEnd;
void PSoC_PostProcessing(void);
void PSoC_DigitalCalc(void);
// General usage PSoC commands; return instantly
void PSoC_SetFingerCapacitance(uint16_t u16FingerCap);
void PSoC_SetFingerCapacitanceFromConfig(void);
// General usage PSoC commands
void PSoC_SetFingerCapacitance(uint16_t u16FingerCap, uint8_t u8Blocking);
void PSoC_SetFingerCapacitanceFromConfig(uint8_t u8Blocking);
/**
* @brief Get the finger capacitance value currently configured on the Master PSoC
*
@ -98,13 +95,24 @@ uint16_t PSoC_GetFingerCapacitance(void);
/**
* @brief Request a debug array from the PSoC pair
*
* This call is asynchronous. Use pu8Ready to ensure pu8Data is ready.
* Only one call can be in-flight at once.
* **DO NOT** call PSoCGetFingerCapacitance while waiting on this function.
*
* @param u8Cmd The specific debug array to request
* @param pu8Data Pointer to array that will receive the data. Must be 32 bytes.
* @param pu8Ready Optional uint8_t to signal the data is ready
*/
void PSoC_GetDebug(PSoC_CMD_DEBUG u8Cmd, uint8_t* pu8Data, volatile uint8_t* pu8Ready);
void PSoC_EnableDebug(uint8_t u8D1, uint8_t u8D2);
void PSoC_GetDebug(PSoC_CMD_DEBUG u8Cmd, uint8_t* pu8Data);
typedef enum : uint8_t {
// Flag 0 doesn't appear to be used anywhere, nor does it appear to have any effect
PSoC_DebugFlag_0 = 0,
PSoC_DebugFlag_TraceReset = 1,
} PSoC_DebugFlag;
/**
* @brief Enable or disable a debug tracing flag
*
* The reception of debug traces will be recorded in:
* - gu32LastCapSenseStart
* - gu32LastCapSenseEnd
*
* @param eFlag Which flag to set
* @param bEnable Should the flag be enabled or disabled
*/
void PSoC_SetDebug(PSoC_DebugFlag eFlag, uint8_t bEnable);

View File

@ -10,7 +10,7 @@ static uint8_t su8AutoEnabledRaw = 0;
static uint8_t su8AutoEnabledByte = 0;
static uint32_t su32SinceLastControlled = 0;
uint8_t gu8GameBrightness = 40; // TODO: Actually make use of this
uint8_t gu8GameBrightness = 40;
static uint16_t su16ByteRawSliderOffset = 0;
static uint8_t su8ByteRawSliderShift = 0;
@ -23,9 +23,11 @@ typedef enum {
SLIDER_PARSE_CHECKSUM,
} slider_parse_state;
// Hardware information for a Chunithm slider
static const slider_cmd_Tx_hw_info sSliderHwInfo = {
"15330 ", 0xA0, "06712", 0xFF, 0x90, 0, 0,
};
void Slider_Exception(uint8_t u8SliderCmd, slider_exception u8Exc);
static inline void Slider_Write(uint8_t u8Byte) {
if (u8Byte == SLIDER_SYNC || u8Byte == SLIDER_MARK) {
@ -47,11 +49,36 @@ static void Slider_Respond(slider_cmd_Tx u8SliderCmd, const uint8_t* pu8Packet,
Slider_Write(-u8Sum);
}
uint8_t u8Null64[64] = { 0 };
static void Slider_Send_Report(void) {
if (gbUIOpen) {
Slider_Respond(SLIDER_CMD_Tx_REPORT, u8Null64, sizeof gu8GroundData);
} else {
Slider_Respond(SLIDER_CMD_Tx_REPORT, gu8GroundData, sizeof gu8GroundData);
}
}
static void Slider_Send_Report_Raw(void) {
if (gbUIOpen) {
Slider_Respond(SLIDER_CMD_Tx_RAW, u8Null64, sizeof gu16PSoCDiff);
} else {
Slider_Respond(SLIDER_CMD_Tx_RAW, (void*)gu16PSoCDiff, sizeof gu16PSoCDiff);
}
}
static void Slider_Send_Report_Byte(void) {
slider_cmd_Tx_raw buffer;
for (uint8_t i = 0; i < 32; i++) {
if (gbUIOpen) {
buffer.u16Raw[i] = 0;
} else {
buffer.u16Raw[i] = (gu16PSoCDiff[i] >> su8ByteRawSliderShift) - su16ByteRawSliderOffset;
}
}
Slider_Respond(SLIDER_CMD_Tx_RAW, (void*)&buffer, sizeof buffer);
}
static void Slider_Process(slider_cmd_Rx u8SliderCmd, uint8_t* pu8Packet, uint8_t u8NPacket) {
switch (u8SliderCmd) {
case SLIDER_CMD_Rx_RESET:
// These three weren't present previously, but PSoC firmware suggests they should be
// TODO: Validate this against the game!
su8AutoEnabled = 0;
su8AutoEnabledRaw = 0;
su8AutoEnabledByte = 0;
@ -92,7 +119,7 @@ static void Slider_Process(slider_cmd_Rx u8SliderCmd, uint8_t* pu8Packet, uint8_
gbLedDataIsControlledExt = 1;
su32SinceLastControlled = 0;
memcpy(gu8aControlledExtLedData, &((slider_cmd_Rx_led*)pu8Packet)->aBRG, 3 * 32);
memcpy(gaControlledExtLedData, &((slider_cmd_Rx_led*)pu8Packet)->aBRG, 3 * 32);
}
// Reprocess this packet as a report request where applicable
if (u8SliderCmd == SLIDER_CMD_Rx_REPORT_PING_PONG) {
@ -105,13 +132,13 @@ static void Slider_Process(slider_cmd_Rx u8SliderCmd, uint8_t* pu8Packet, uint8_
return;
case SLIDER_CMD_Rx_REPORT:
Slider_Respond(SLIDER_CMD_Tx_REPORT, gu8GroundData, sizeof gu8GroundData);
Slider_Send_Report();
return;
case SLIDER_CMD_Rx_RAW:
// TODO:
Slider_Send_Report_Raw();
return;
case SLIDER_CMD_Rx_BRAW:
// TODO:
Slider_Send_Report_Byte();
return;
case SLIDER_CMD_Rx_REPORT_ENABLE:
@ -143,6 +170,92 @@ static void Slider_Process(slider_cmd_Rx u8SliderCmd, uint8_t* pu8Packet, uint8_
su8ByteRawSliderShift = ((slider_cmd_Rx_braw_set_shift*)pu8Packet)->u8Shift;
Slider_Respond(SLIDER_CMD_Tx_BRAW_SET_SHIFT, NULL, 0);
return;
case SLIDER_CMD_Rx_DEBUG:
uint8_t u8aData[32];
switch ((slider_debug_cmd_Rx)pu8Packet[0]) {
case SLIDER_DEBUG_CMD_Rx_GET_FINGER_CAP:
uint16_t u16FingerCap = PSoC_GetFingerCapacitance();
Slider_Respond(SLIDER_CMD_Tx_DEBUG, (uint8_t*)&u16FingerCap,
sizeof u16FingerCap);
break;
case SLIDER_DEBUG_CMD_Rx_TRACE_RESET:
// TODO: Broken. Blocks forever.
PSoC_SetDebug(PSoC_DebugFlag_TraceReset, 1);
Slider_Respond(SLIDER_CMD_Tx_DEBUG, NULL, 0);
break;
case SLIDER_DEBUG_CMD_Rx_GET_LAST_CS_START:
Slider_Respond(SLIDER_CMD_Tx_DEBUG, (uint8_t*)&gu32LastCapSenseStart,
sizeof gu32LastCapSenseStart);
break;
case SLIDER_DEBUG_CMD_Rx_GET_LAST_CS_END:
Slider_Respond(SLIDER_CMD_Tx_DEBUG, (uint8_t*)&gu32LastCapSenseEnd,
sizeof gu32LastCapSenseEnd);
break;
case SLIDER_DEBUG_CMD_Rx_PSoC_REQUEST_DEBUG:
if (u8NPacket == 2) {
PSoC_GetDebug(pu8Packet[1], u8aData);
}
Slider_Respond(SLIDER_CMD_Tx_DEBUG, u8aData, sizeof u8aData);
break;
case SLIDER_DEBUG_CMD_Rx_HOST_FMC_READ:
if (u8NPacket == 5) {
// We can't use a cast to uint32_t* because of unaligned reads!
uint32_t u32Base = pu8Packet[1];
u32Base |= pu8Packet[2] << 8;
u32Base |= pu8Packet[3] << 16;
u32Base |= pu8Packet[4] << 24;
memset(u8aData, 0xFF, sizeof u8aData);
FMC_Open();
FMC_ReadData(u32Base, u32Base + sizeof u8aData, (void*)u8aData);
FMC_Close();
}
Slider_Respond(SLIDER_CMD_Tx_DEBUG, u8aData, sizeof u8aData);
break;
case SLIDER_DEBUG_CMD_Rx_LED_FMC_READ:
uint32_t u32Data = 0xFFFFFFFF;
if (u8NPacket == 5) {
uint32_t u32Offset = pu8Packet[1];
u32Offset |= pu8Packet[2] << 8;
u32Offset |= pu8Packet[3] << 16;
u32Offset |= pu8Packet[4] << 24;
LED_FMC_Read(u32Offset, &u32Data);
}
Slider_Respond(SLIDER_CMD_Tx_DEBUG, (uint8_t*)&u32Data, sizeof u32Data);
break;
case SLIDER_DEBUG_CMD_Rx_HOST_ENTER_LDROM:
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:
Slider_Respond(SLIDER_CMD_Tx_DEBUG, (uint8_t*)&gbLedIsCustom,
sizeof gbLedIsCustom);
break;
case SLIDER_DEBUG_CMD_Rx_LED_GET_DIGITAL:
Slider_Respond(SLIDER_CMD_Tx_DEBUG, &gu8DigitalButtons,
sizeof gu8DigitalButtons);
break;
default:
Slider_Exception(u8SliderCmd, SLIDER_EXCEPTION_BUS_ERROR);
break;
}
return;
default:
Slider_Exception(u8SliderCmd, SLIDER_EXCEPTION_BUS_ERROR);
break;
}
}
void Slider_Exception(uint8_t u8SliderCmd, slider_exception u8Exc) {
@ -233,13 +346,37 @@ void Slider_Tick1ms() {
if (++su32SinceLastControlled == 5 * 1000) gbLedDataIsControlledExt = 0;
}
if (su8AutoEnabled) {
static uint8_t u8Counter = 0;
// Only actually send an update every 8ms
if (++u8Counter != 8) return;
static uint16_t u16Counter = 0;
/**
* I haven't totally tracked down the source of the interval timer on a real slider.
* That said, from measurement it's 15.365ms or so. The exact time will be based on
* a counter from one of the low speed clocks.
*
* The game makes calls to ReadFile on an 8ms interval, but this isn't the most stable.
* I took a short capture, and my intervals were:
*
* 0% | 3.6ms
* 10% | 7.0ms
* 50% | 8.0ms
* 90% | 9.0ms
* 100% | 14.0ms
*
* This +-1ms appears to be caused by the use of Sleep() to regulate the interval, which
* has an argument precision of 1ms (and an overall precision far worse!).
*
* Chunithm will retry a read four times, at which point it considers the slider to have
* timed out (error 3100).
*
* Based on this, we should be safe to indeed run at a 15ms interval here.
*
* I received one report of a user getting a 3100 when they started the game. I am current
* working on the basis that this is a one-off, however this will need re-addressed if this
* issue becomes widespread.
*/
if (++u16Counter != 15) return;
u16Counter = 0;
u8Counter = 0;
Slider_Respond(SLIDER_CMD_Tx_REPORT, gu8GroundData, sizeof gu8GroundData);
}
if (su8AutoEnabled) Slider_Send_Report();
if (su8AutoEnabledRaw) Slider_Send_Report_Raw();
if (su8AutoEnabledByte) Slider_Send_Report_Byte();
}

View File

@ -52,7 +52,7 @@
#define PAD_31_Msk BIT0
#define PAD_32_Msk BIT1
typedef enum {
typedef enum : uint8_t {
// =========================
// Actually used by Chunithm
// =========================
@ -69,9 +69,14 @@ typedef enum {
/* Retrieve hardware information (model number, etc.) */
SLIDER_CMD_Rx_HW_INFO = 0xF0,
// ======
// Autism
// ======
// ==========================================
// Custom additions, used for debugging, etc.
// ==========================================
SLIDER_CMD_Rx_DEBUG = 0xF1,
// =====================================================================
// Required for a complete slider implementation, but unused by the game
// =====================================================================
/* Request a single standard report */
SLIDER_CMD_Rx_REPORT = 0x01,
/* Set LED brightness and BRG values, with a report as the response */
@ -98,7 +103,27 @@ typedef enum {
/* Request the CPU status registers */
SLIDER_CMD_Rx_CPU_STATUS = 0xE0,
} slider_cmd_Rx;
typedef enum {
typedef enum : uint8_t {
// Actual debugging stuff
SLIDER_DEBUG_CMD_Rx_GET_FINGER_CAP = 0x00,
SLIDER_DEBUG_CMD_Rx_TRACE_RESET = 0x01,
SLIDER_DEBUG_CMD_Rx_GET_LAST_CS_START = 0x02,
SLIDER_DEBUG_CMD_Rx_GET_LAST_CS_END = 0x03,
SLIDER_DEBUG_CMD_Rx_PSoC_REQUEST_DEBUG = 0x04,
// Flash access
SLIDER_DEBUG_CMD_Rx_HOST_FMC_READ = 0x10,
SLIDER_DEBUG_CMD_Rx_LED_FMC_READ = 0x12,
// Chip reset
SLIDER_DEBUG_CMD_Rx_HOST_ENTER_LDROM = 0x20,
SLIDER_DEBUG_CMD_Rx_LED_ENTER_LDROM = 0x21,
SLIDER_DEBUG_CMD_Rx_LED_CHECK = 0x22,
// IO Access
SLIDER_DEBUG_CMD_Rx_LED_GET_DIGITAL = 0x30,
} slider_debug_cmd_Rx;
typedef enum : uint8_t {
SLIDER_CMD_Tx_REPORT = 0x01,
SLIDER_CMD_Tx_REPORT_DISABLE = 0x04,
@ -112,8 +137,10 @@ typedef enum {
SLIDER_CMD_Tx_CPU_STATUS = 0xE0,
SLIDER_CMD_Tx_EXCEPTION = 0xEE,
SLIDER_CMD_Tx_HW_INFO = 0xF0,
SLIDER_CMD_Tx_DEBUG = 0xF1,
} slider_cmd_Tx;
typedef enum {
typedef enum : uint8_t {
SLIDER_EXCEPTION_CHECKSUM = 1,
SLIDER_EXCEPTION_BUS_ERROR = 2,
} slider_exception;
@ -126,7 +153,7 @@ typedef enum {
#define SLIDER_EXCEPTION_CTX_GENERIC 0xED
typedef struct __packed {
uint8_t u8Brightness;
uint8_t u8Brightness; // Range: 0~63
struct __packed {
uint8_t u8B;
uint8_t u8R;
@ -184,6 +211,6 @@ typedef struct __packed {
};
} slider_cmd_Tx_cpu_status;
extern uint8_t gu8GameBrightness; // 0~63
extern uint8_t gu8GameBrightness; // Range: 0~63
void Slider_TickSerial(void);
void Slider_Tick1ms(void);

164
src/sys.c
View File

@ -17,10 +17,16 @@ void SYS_ResetModule(uint32_t u32ModuleIndex) {
~(1 << (u32ModuleIndex & 0x00ffffff));
}
// Even though we have an external 12MHz crystal, it's never actually used.
// This define toggles if we will even bother configuring it or not.
#define EXT_CLK
void SYS_Init(void) {
#ifdef EXT_CLK
// Enable XT1_OUT (PF.0) and XT1_IN (PF.1)
SYS->GPF_MFP &= ~(SYS_GPF_MFP_PF0_Msk | SYS_GPF_MFP_PF1_Msk);
SYS->GPF_MFP |= SYS_GPF_MFP_PF0_XT1_OUT | SYS_GPF_MFP_PF1_XT1_IN;
#endif
// Enable Internal RC 22.1184 MHz clock
CLK_EnableXtalRC(CLK_PWRCON_OSC22M_EN_Msk);
@ -29,12 +35,14 @@ void SYS_Init(void) {
// Switch HCLK clock source to Internal RC and HCLK source divide 1
CLK_SetHCLK(CLK_CLKSEL0_HCLK_S_HIRC, CLK_CLKDIV_HCLK(1));
#ifdef EXT_CLK
// Enable external XTAL 12 MHz clock
CLK_EnableXtalRC(CLK_PWRCON_XTL12M_EN_Msk);
CLK_WaitClockReady(CLK_CLKSTATUS_XTL12M_STB_Msk);
#endif
// Set core clock
CLK_SetCoreClock(72000000);
CLK_SetCoreClock(72 MHz);
// Enable module clocks
CLK_EnableModuleClock(UART1_MODULE);
@ -57,6 +65,8 @@ void SYS_Init(void) {
PIN_SCL = 1;
// Configure our GPIO pins
GPIO_SetMode(_PIN_FN1, GPIO_PMD_QUASI);
GPIO_SetMode(_PIN_FN2, GPIO_PMD_QUASI);
GPIO_SetMode(_PIN_EC1, GPIO_PMD_INPUT);
GPIO_SetMode(_PIN_RX2, GPIO_PMD_INPUT);
GPIO_SetMode(_PIN_RX3, GPIO_PMD_INPUT);
@ -71,26 +81,30 @@ void SYS_Init(void) {
PIN_LED_WING_PWR = 1;
PIN_LED_GROUND_PWR = 1;
GPIO_SetMode(_PIN_FN2, GPIO_PMD_INPUT);
// TODO:
// 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).
//
// We have 145us to get to this point, because that's how long the LED bootloader is willing to
// wait!
if (PIN_FN2 == 0) {
PIN_USB_MUX_SEL = USB_MUX_LEDS;
PIN_USB_MUX_EN = USB_MUX_ENABLE;
// Trigger the bootloader
GPIO_SetMode(_PIN_SDA, GPIO_PMD_OUTPUT);
GPIO_SetMode(_PIN_SCL, GPIO_PMD_OUTPUT);
PIN_SDA = 0;
PIN_SCL = 0;
return;
} else {
// Set GPA multi-function pins for I2C1 SDA and SCL
SYS->GPA_MFP &= ~(SYS_GPA_MFP_PA10_Msk | SYS_GPA_MFP_PA11_Msk);
SYS->GPA_MFP |= (SYS_GPA_MFP_PA10_I2C1_SDA | SYS_GPA_MFP_PA11_I2C1_SCL);
SYS->ALT_MFP &= ~(SYS_ALT_MFP_PA10_Msk | SYS_ALT_MFP_PA11_Msk);
SYS->ALT_MFP |= (SYS_ALT_MFP_PA10_I2C1_SDA | SYS_ALT_MFP_PA11_I2C1_SCL);
PIN_USB_MUX_SEL = USB_MUX_HOST;
PIN_USB_MUX_EN = USB_MUX_ENABLE;
}
// Set GPA multi-function pins for I2C1 SDA and SCL
SYS->GPA_MFP &= ~(SYS_GPA_MFP_PA10_Msk | SYS_GPA_MFP_PA11_Msk);
SYS->GPA_MFP |= (SYS_GPA_MFP_PA10_I2C1_SDA | SYS_GPA_MFP_PA11_I2C1_SCL);
SYS->ALT_MFP &= ~(SYS_ALT_MFP_PA10_Msk | SYS_ALT_MFP_PA11_Msk);
SYS->ALT_MFP |= (SYS_ALT_MFP_PA10_I2C1_SDA | SYS_ALT_MFP_PA11_I2C1_SCL);
PIN_USB_MUX_EN = USB_MUX_ENABLE;
PIN_USB_MUX_SEL = USB_MUX_HOST;
}
void SYS_ModuleInit(void) {
@ -130,13 +144,46 @@ void SYS_ModuleInit(void) {
NVIC_EnableIRQ(USBD_IRQn);
}
// Define a dedicated symbol for this, so we can easily trap it with a debugger!
void HardFault_Handler() {
void HardFault_Handler(uint32_t *stack) {
// Extract r0-3, r12, lr, pc, psr, and place them into "stack"
asm("MOVS r0, #4 \n"
"MOV r1, lr \n"
"TST r0, r1 \n" // check LR bit 2
"BEQ 1f \n" // stack use MSP
"MRS r0, psp \n" // stack use PSP, read PSP
"MOV r1, lr \n" // LR current value
"B 2f \n"
"1: \n"
"MRS r0, msp \n" // LR current value
"2: \n");
// Extract out into locals for easy debugging
uint32_t r0 = stack[0];
uint32_t r1 = stack[1];
uint32_t r2 = stack[2];
uint32_t r3 = stack[3];
uint32_t r12 = stack[4];
uint32_t lr = stack[5];
uint32_t pc = stack[6];
uint32_t psr = stack[7];
// Silence unused warnings
(void)r0;
(void)r1;
(void)r2;
(void)r3;
(void)r12;
(void)lr;
(void)pc;
(void)psr;
// Force a chip reset
SYS->IPRSTC1 = SYS_IPRSTC1_CHIP_RST_Msk;
while (1)
;
}
void SYS_Bootloader_Check() {
void SYS_Bootloader_Check(void) {
FMC_Open();
while (FMC_Read(BOOTLOADER_MAGIC_ADDR) != BOOTLOADER_MAGIC)
;
@ -156,3 +203,88 @@ void TMR0_IRQHandler(void) {
}
}
}
#define V6M_AIRCR_VECTKEY_DATA 0x05FA0000UL
void __attribute__((noreturn)) SYS_EnterLDROM(void) {
SYS_UnlockReg();
// 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
// flags, though realistically we are.
// An MCU reset guarantees we end up in LDROM regardless of configuration.
// Reset to LDROM using a CPU reset
// FMC->ISPCON |= FMC_ISPCON_BS_Msk | FMC_ISPCON_ISPEN_Msk;
// SYS->IPRSTC1 |= SYS_IPRSTC1_CPU_RST_Msk;
// Reset to LDROM using a CHIP reset
// SYS->IPRSTC1 |= SYS_IPRSTC1_CHIP_RST_Msk;
// Reset to LDROM using a MCU reset
SYS->RSTSRC = SYS_RSTSRC_RSTS_POR_Msk | SYS_RSTSRC_RSTS_RESET_Msk;
FMC->ISPCON |= FMC_ISPCON_BS_Msk | FMC_ISPCON_ISPEN_Msk;
NVIC_SystemReset();
// Trap the processor
while (1)
;
__builtin_unreachable();
}
void SYS_WaitBootloaderLED(void) {
SYS_UnlockReg();
// 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);
// Trigger the bootloader
GPIO_SetMode(_PIN_SDA, GPIO_PMD_OUTPUT);
GPIO_SetMode(_PIN_SCL, GPIO_PMD_OUTPUT);
PIN_SDA = 0;
PIN_SCL = 0;
// Shutdown I2C
I2C_Close(I2C1);
// 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);
// Switch the USB connection over the LED microcontroller
PIN_USB_MUX_SEL = USB_MUX_LEDS;
PIN_USB_MUX_EN = USB_MUX_ENABLE;
u16I2CRxIndex = 0;
// Wait 5 seconds, which is long enough for the LED bootloader to take over
CLK_SysTickLongDelay(5000 ms);
// Switch PA10 and PA11 back to I2C
SYS->GPA_MFP &= ~(SYS_GPA_MFP_PA10_Msk | SYS_GPA_MFP_PA11_Msk);
SYS->GPA_MFP |= (SYS_GPA_MFP_PA10_I2C1_SDA | SYS_GPA_MFP_PA11_I2C1_SCL);
SYS->ALT_MFP &= ~(SYS_ALT_MFP_PA10_Msk | SYS_ALT_MFP_PA11_Msk);
SYS->ALT_MFP |= (SYS_ALT_MFP_PA10_I2C1_SDA | SYS_ALT_MFP_PA11_I2C1_SCL);
// Restart the I2C controller
LED_I2C1_Init();
// Wait until we start getting data from the LED firmware
while (u16I2CRxIndex == 0)
;
// Take back control of USB
PIN_USB_MUX_SEL = USB_MUX_HOST;
PIN_USB_MUX_EN = USB_MUX_ENABLE;
// Bring our USB PHY back online, along with a delay sufficient for a device reconnect to be
// detected
Tas_USBD_Open();
Tas_USBD_Init();
Tas_USBD_Start();
NVIC_EnableIRQ(USBD_IRQn);
}

View File

@ -3,39 +3,33 @@
#include <stdio.h>
#include <string.h>
#if defined(__CC_ARM)
#elif defined(__GNUC__)
#define __packed __attribute__((packed))
#else
#error Unknown compiler
#endif
#include "_compiler.h"
#define ms *1000
#define kHz *1000
#define MHz *1000000
/* === LEDs === */
#define LED_Tx_BUFFER 254
// Defined as volatile due to the I2C interrupt reading at random times!
extern volatile uint8_t gu8LEDTx[LED_Tx_BUFFER];
extern uint32_t gu32NowMs;
extern uint8_t gbUIOpen;
/* === DAO-DRM === */
#define BYTESWAP_U16(x) ((((x) & 0xFF) << 8) | ((x) >> 8))
// === DAO-DRM ===
// * For now the bootloader check is being left enabled.
// * It'll help catch if I break that in my bootloader :P
#define ENABLE_BOOTLOADER_CHECK
#define BOOTLOADER_MAGIC 0x5555A320
#define BOOTLOADER_MAGIC_ADDR 0x100FF8
/* === USB definitions === */
#include "usb_inc/hid.h"
#include "usb_inc/keymap.h"
#include "usb_inc/usb.h"
/* === Local files === */
// === Local files ===
#include "fmc_user.h"
#include "led.h"
#include "pins.h"
#include "psoc.h"
#include "slider.h"
#include "usb_def.h"
#include "vcom.h"
#include "io4.h"
#define IO4_VENDOR "SEGA INTERACTIVE"
// Product description
@ -48,102 +42,26 @@ extern volatile uint8_t gu8LEDTx[LED_Tx_BUFFER];
"6679A;" /* Chip number */ \
"00;" /* Config */
// Functional description
#define _IO4_FUNCTION \
"GOUT=14_" /* General purpose output (20) */ \
"ADIN=8,E_" /* ADC input (8) */ \
"ROTIN=4_" /* Rotary input (4) */ \
"COININ=2_" /* Coin input (2) */ \
"SWIN=2,E_" /* Switch inputs (2) */ \
"UQ1=41,6;" /* Unique function 1 (?) */
#define _IO4_FUNCTION \
"GOUT=14_" /* General purpose output (20) */ \
"ADIN=8,E_" /* ADC input (8) */ \
"ROTIN=4_" /* Rotary input (4) */ \
"COININ=2_" /* Coin input (2) */ \
"SWIN=2,E_" /* Switch inputs (2) */ \
"UQ1=41,6;" /* Unique 1 (Command: 41h, bytes 6) */
#define IO4_PRODUCT _IO4_PRODUCT _IO4_FUNCTION
#define IO4_VID 0x0CA3
#define IO4_PID 0x0021
#define INCR(x, y) ((x) = (x) < (y) ? (x) + 1 : (y))
#define DECR(x, y) ((x) = (x) > (y) ? (x)-1 : (y))
#define MOD_INCR(x, y) ((x) = (x) == ((y)-1) ? 0 : ((x) + 1))
#define MOD_DECR(x, y) ((x) = (x) == 0 ? ((y)-1) : ((x)-1))
#define INV(x) ((x) = 1 - (x))
extern volatile uint8_t gu8VcomReady;
/**
* WARNING: This is both used internally and sent verbatim over serial
* It **MUST** be 32 bytes of ground data
*/
extern uint8_t gu8GroundData[32];
/* === For HID === */
enum {
HID_REPORT_ID_IO4 = 1,
HID_REPORT_ID_KEYBOARD,
HID_REPORT_ID_DEBUG_A,
HID_REPORT_ID_DEBUG_B,
HID_REPORT_ID_IO4_CMD = 16,
};
#define NUM_FN 2
#define NUM_AIR 6
#define NUM_GROUND 32
typedef struct __packed {
uint8_t bReportId;
uint8_t bKeyboard[NUM_FN + NUM_AIR + NUM_GROUND];
} hid_report_t;
typedef struct __packed {
uint8_t bReportId;
uint16_t wADC[8];
uint16_t wRotary[4];
uint16_t wCoin[2];
uint16_t wButtons[2];
uint8_t bSystemStatus;
uint8_t bUsbStatus;
uint8_t bUnique[29];
} io4_hid_in_t;
typedef struct __packed {
uint8_t bReportId;
uint8_t bCmd;
uint8_t bData[62];
} io4_hid_out_t;
typedef struct __packed {
uint8_t bReportId;
uint16_t wData[16];
} debug_hid_report_t;
extern uint8_t gu8DigitalButtons;
// void HID_Tick();
void USBD_HID_PrepareReport();
uint8_t *USBD_HID_GetReport(uint8_t u8ReportId, uint32_t *pu32Size);
void USBD_HID_SetReport(volatile uint8_t *pu8EpBuf, uint32_t u32Size);
// For CDC
typedef struct __packed {
uint32_t u32DTERate; // Baud rate
uint8_t u8CharFormat; // Stop bit
uint8_t u8ParityType; // Parity
uint8_t u8DataBits; // Data bits
} STR_VCOM_LINE_CODING;
extern volatile int8_t gi8BulkOutReady;
extern STR_VCOM_LINE_CODING gLineCoding;
extern uint16_t gCtrlSignal;
extern volatile uint16_t comRbytes;
extern volatile uint16_t comRhead;
extern volatile uint16_t comRtail;
extern volatile uint16_t comTbytes;
extern volatile uint16_t comThead;
extern volatile uint16_t comTtail;
extern volatile uint8_t *gpu8RxBuf;
extern volatile uint32_t gu32RxSize;
extern volatile uint32_t gu32TxSize;
// For HID
extern uint8_t volatile gu8HIDIO4Ready;
extern uint8_t volatile gu8HIDMiscReady;
// General USB control
extern uint8_t volatile g_u8Suspend;
extern uint8_t g_u8Idle;
extern uint8_t g_u8Protocol;
extern const char *gszVendorInitial;
// === descriptors.c, used in usbd_driver.c ===
extern const char *gszVendor;
extern const char *gszProduct;
extern const usb_device_descr_t *gpDeviceDescriptor;
@ -155,128 +73,28 @@ extern const uint32_t gu32UsbHidMiscReportLen;
extern const uint8_t *gpu8UsbHidIO4Report;
extern const uint8_t *gpu8UsbHidMiscReport;
/*-------------------------------------------------------------*/
// Interfaces
enum {
USBD_ITF_CDC_CMD,
USBD_ITF_CDC_DAT,
USBD_ITF_HID_IO4,
USBD_ITF_HID_MISC,
_USBD_ITF_MAX,
};
// Endpoint number mapping
#define EP_CTRL_IN EP0
#define EP_CTRL_OUT EP1
#define EP_CDC_IN EP2
#define EP_CDC_OUT EP3
#define EP_CDC_CMD EP4
#define EP_HID_IO4_IN EP5
#define EP_HID_MISC_IN EP6
#define EP_HID_MISC_OUT EP7
#define _USBD_INTSTS(x) USBD_INTSTS_EP##x
#define USBD_INTSTS(x) _USBD_INTSTS(x)
// Must match the above!!
#define USBD_INTSTS_CTRL_IN USBD_INTSTS(EP_CTRL_IN)
#define USBD_INTSTS_CTRL_OUT USBD_INTSTS(EP_CTRL_OUT)
#define USBD_INTSTS_CDC_IN USBD_INTSTS(EP_CDC_IN)
#define USBD_INTSTS_CDC_OUT USBD_INTSTS(EP_CDC_OUT)
#define USBD_INTSTS_CDC_CMD USBD_INTSTS(EP_CDC_CMD)
#define USBD_INTSTS_HID_IO4_IN USBD_INTSTS(EP_HID_IO4_IN)
#define USBD_INTSTS_HID_MISC_IN USBD_INTSTS(EP_HID_MISC_IN)
#define USBD_INTSTS_HID_MISC_OUT USBD_INTSTS(EP_HID_MISC_OUT)
#define USBD_CDC_EP_IN (1 | EP_INPUT)
#define USBD_CDC_EP_OUT (2 | EP_OUTPUT)
#define USBD_CDC_EP_CMD (3 | EP_INPUT)
#define USBD_HID_IO4_EP_IN (4 | EP_INPUT)
#define USBD_HID_MISC_EP_IN (5 | EP_INPUT)
#define USBD_HID_MISC_EP_OUT (6 | EP_OUTPUT)
#define USBD_SETUP_BUF_LEN (8)
#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)
// 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
#define SETUP_BUF_BASE 0
#define SETUP_BUF_LEN 8
#if (SETUP_BUF_LEN + EP0_MAX_PKT_SIZE + EP1_MAX_PKT_SIZE + EP2_MAX_PKT_SIZE + EP3_MAX_PKT_SIZE + \
EP4_MAX_PKT_SIZE + EP5_MAX_PKT_SIZE + EP6_MAX_PKT_SIZE + EP7_MAX_PKT_SIZE) > 512
#error USB endpoint packet sizes exceeds 512-byte maximum
#endif
#define EP0_BUF_BASE (SETUP_BUF_BASE + SETUP_BUF_LEN)
#define EP1_BUF_BASE (EP0_BUF_BASE + EP0_MAX_PKT_SIZE)
#define EP2_BUF_BASE (EP1_BUF_BASE + EP1_MAX_PKT_SIZE)
#define EP3_BUF_BASE (EP2_BUF_BASE + EP2_MAX_PKT_SIZE)
#define EP4_BUF_BASE (EP3_BUF_BASE + EP3_MAX_PKT_SIZE)
#define EP5_BUF_BASE (EP4_BUF_BASE + EP4_MAX_PKT_SIZE)
#define EP6_BUF_BASE (EP5_BUF_BASE + EP5_MAX_PKT_SIZE)
#define EP7_BUF_BASE (EP5_BUF_BASE + EP6_MAX_PKT_SIZE)
// Define Descriptor information
#define HID_IO4_INT_IN_INTERVAL 8
#define HID_DEFAULT_INT_IN_INTERVAL 1
#define HID_DEFAULT_INT_OUT_INTERVAL 1
#define USBD_SELF_POWERED 0
#define USBD_REMOTE_WAKEUP 0
#define USBD_MAX_POWER (500 / 2)
/*-------------------------------------------------------------*/
#define HEX_NIBBLE(x) ("0123456789ABCDEF"[(x)&0xf])
// === sys.c ===
void SYS_Init(void);
void SYS_Bootloader_Check(void);
void SYS_ModuleInit(void);
void SYS_EnterLDROM(void);
void SYS_WaitBootloaderLED(void);
extern volatile uint8_t gu8Do250usTick;
extern volatile uint8_t gu8Do1msTick;
void EP_CDC_CMD_Handler(void);
void EP_CDC_OUT_Handler(void);
void EP_CDC_IN_Handler(void);
void EP_HID_IO4_IN_Handler(void);
void EP_HID_MISC_IN_Handler(void);
void EP_HID_MISC_OUT_Handler(void);
void USB_VCOM_Write(uint8_t u8Char);
uint16_t USB_VCOM_Available(void);
uint8_t USB_VCOM_Read(void);
void USB_VCOM_Tick(void);
void USB_VCOM_PurgeTx(void);
// === ui.c ===
void UI_Tick(void);
void DelayCycles(uint32_t u32Cycles);
#define DelayCycles_Small(x) \
for (int i = 0; i < (x); i++) __asm__ volatile("" : "+g"(i) : :);
// === Misc ===
#define HEX_NIBBLE(x) ("0123456789ABCDEF"[(x)&0xf])
#define DELAY_CYCLES_2 __asm__ volatile("NOP\nNOP\n");
// Custom USBD implementation
void Tas_USBD_Open(void);
void Tas_USBD_Init(void);
void Tas_USBD_Start(void);
void Tas_USBD_ClassRequest(void);
void Tas_USBD_GetSetupPacket(usb_setup_t *buf);
void Tas_USBD_ProcessSetupPacket(void);
void Tas_USBD_PrepareCtrlIn(void *pu8Buf, uint32_t u32Size);
void Tas_USBD_CtrlIn(void);
void Tas_USBD_PrepareCtrlOut(void *pu8Buf, uint32_t u32Size,
void (*pCallback)(volatile uint8_t *, uint32_t));
void Tas_USBD_CtrlOut(void);
void Tas_USBD_SwReset(void);
extern volatile uint8_t *g_usbd_CtrlInPointer;
extern volatile uint32_t g_usbd_CtrlInSize;
#define INCR(x, y) ((x) = (x) < (y) ? (x) + 1 : (y))
#define DECR(x, y) ((x) = (x) > (y) ? (x)-1 : (y))
#define MOD_INCR(x, y) ((x) = (x) == ((y)-1) ? 0 : ((x) + 1))
#define MOD_DECR(x, y) ((x) = (x) == 0 ? ((y)-1) : ((x)-1))
#define INV(x) ((x) = 1 - (x))
#define MS_SINCE(x) \
((gu32NowMs >= (x)) ? /* We haven't wrapped yet */ (gu32NowMs - (x)) \
: /* We've wrapped over (50 days!) */ (gu32NowMs + (0xFFFFFFFF - (x))))

338
src/ui.c
View File

@ -1,7 +1,8 @@
#include "tasoller.h"
#define FN2_HOLD_TIME 250 // ms of FN2 to enter config
#define FN1_HOLD_TIME 2000 // ms of FN1 to enter calibration
#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
// All three of these are scaled based on gConfig.u8Sens
#define CALIB_HANDS_MIN 100 // If we read less than this, ignore it
@ -12,14 +13,17 @@
#define GREEN 120
#define BLUE 240
uint8_t gbUIOpen = 0;
static uint8_t u8TestIsActive = 0;
static void UI_TickSensitivity(void) {
for (uint8_t i = 0; i < LED_NUM_GROUND; i++) {
gaControlledIntLedData[i].u16H = 0;
gaControlledIntLedData[i].u8S = (gConfig.u8Sens - 1) * 16;
gaControlledIntLedData[i].u8V = 0;
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;
}
for (uint8_t i = 0; i < gConfig.u8Sens; i++) gaControlledIntLedData[i * 2].u8V = 255;
for (uint8_t i = 0; i < gConfig.u8Sens; i++) gaControlledIntLedData[i * 2].v = 255;
// Preferentially allow decreasing of sensitivity, in case it's been taken up too high
if (gu32PSoCDigitalPos & PAD_26_Msk && gConfig.u8Sens > 1) {
@ -29,9 +33,9 @@ static void UI_TickSensitivity(void) {
}
}
static uint8_t su8SensTimeout = 0;
static void UI_TickSettings(void) {
// If either of the sensitivity settings are being changed, just render that
static uint8_t su8SensTimeout = 0;
if (gu16PSoCDigital & CELL_12_Msk) {
su8SensTimeout = 150;
UI_TickSensitivity();
@ -41,27 +45,35 @@ static void UI_TickSettings(void) {
UI_TickSensitivity();
// Save the sensitivity on exit
if (su8SensTimeout == 0) PSoC_SetFingerCapacitanceFromConfig();
if (su8SensTimeout == 0) PSoC_SetFingerCapacitanceFromConfig(1);
return;
}
for (uint8_t i = 0; i < LED_NUM_GROUND; i++) {
gaControlledIntLedData[i].u16H = 0;
gaControlledIntLedData[i].u8S = 255;
gaControlledIntLedData[i].u8V = 0;
static uint8_t u8Pulser = 0;
static uint8_t u8PulserDir = 0;
if (u8PulserDir) {
if (--u8Pulser == 0) u8PulserDir = 0;
} else {
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;
}
{ // LED colour control
gaControlledIntLedData[LED_CELL_0].u16H = gConfig.u16HueWingLeft;
gaControlledIntLedData[LED_CELL_0].u8V = 255;
gaControlledIntLedData[LED_CELL_1].u16H = gConfig.u16HueGround;
gaControlledIntLedData[LED_CELL_1].u8V = 255;
gaControlledIntLedData[LED_DIVIDER_1_2].u16H = gConfig.u16HueGroundActive;
gaControlledIntLedData[LED_DIVIDER_1_2].u8V = 255;
gaControlledIntLedData[LED_CELL_2].u16H = gConfig.u16HueGround;
gaControlledIntLedData[LED_CELL_2].u8V = 255;
gaControlledIntLedData[LED_CELL_3].u16H = gConfig.u16HueWingRight;
gaControlledIntLedData[LED_CELL_3].u8V = 255;
gaControlledIntLedData[LED_CELL_0].h = gConfig.u16HueWingLeft;
gaControlledIntLedData[LED_CELL_0].v = 255;
gaControlledIntLedData[LED_CELL_1].h = gConfig.u16HueGround;
gaControlledIntLedData[LED_CELL_1].v = 255;
gaControlledIntLedData[LED_DIVIDER_1_2].h = gConfig.u16HueGroundActive;
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].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);
@ -77,27 +89,27 @@ static void UI_TickSettings(void) {
static uint16_t su16Hue = 0;
MOD_INCR(su16Hue, LED_HUE_MAX * 5);
if (gConfig.bEnableRainbow) {
gaControlledIntLedData[LED_CELL_5].u16H = su16Hue / 5;
gaControlledIntLedData[LED_CELL_5].u8V = 255;
gaControlledIntLedData[LED_CELL_5].h = su16Hue / 5;
gaControlledIntLedData[LED_CELL_5].v = 255;
} else {
gaControlledIntLedData[LED_CELL_5].u8S = 0;
gaControlledIntLedData[LED_CELL_5].u8V = 255;
gaControlledIntLedData[LED_CELL_5].s = 0;
gaControlledIntLedData[LED_CELL_5].v = 255;
}
if (gu16PSoCDigitalPos & CELL_5_Msk) INV(gConfig.bEnableRainbow);
}
{ // Brightness
if (gConfig.u8LedGroundBrightness) {
gaControlledIntLedData[LED_CELL_6].u8S = 0;
gaControlledIntLedData[LED_CELL_6].u8V = gConfig.u8LedGroundBrightness;
gaControlledIntLedData[LED_CELL_6].s = 0;
gaControlledIntLedData[LED_CELL_6].v = gConfig.u8LedGroundBrightness;
} else {
gaControlledIntLedData[LED_CELL_6].u8V = 255;
gaControlledIntLedData[LED_CELL_6].v = 255;
}
if (gConfig.u8LedWingBrightness) {
gaControlledIntLedData[LED_CELL_7].u8S = 0;
gaControlledIntLedData[LED_CELL_7].u8V = gConfig.u8LedWingBrightness;
gaControlledIntLedData[LED_CELL_7].s = 0;
gaControlledIntLedData[LED_CELL_7].v = gConfig.u8LedWingBrightness;
} else {
gaControlledIntLedData[LED_CELL_7].u8V = 255;
gaControlledIntLedData[LED_CELL_7].v = 255;
}
if (gu32PSoCDigitalTrig & PAD_13_Msk) INCR(gConfig.u8LedGroundBrightness, 255);
@ -105,145 +117,201 @@ static void UI_TickSettings(void) {
if (gu32PSoCDigitalTrig & PAD_15_Msk) INCR(gConfig.u8LedWingBrightness, 255);
if (gu32PSoCDigitalTrig & PAD_16_Msk) DECR(gConfig.u8LedWingBrightness, 0);
}
// [Cell 8,9,10,11 no function]
// [Cell 8 no function]
{ // Consumer control
gaControlledIntLedData[LED_CELL_9].s = 0;
gaControlledIntLedData[LED_CELL_9].v = 255;
if (u32EnterPressStarted != 0xFFFFFFFF) {
gaControlledIntLedData[LED_CELL_10].s = 0;
gaControlledIntLedData[LED_CELL_10].v = u8Pulser;
} else {
gaControlledIntLedData[LED_CELL_10].s = 0;
gaControlledIntLedData[LED_CELL_10].v = 255;
}
if (gu32PSoCDigital & PAD_20_Msk) {
u16RequestedConsumerControl = MEDIA_VOLUME_DOWN;
} else if (gu32PSoCDigital & PAD_19_Msk) {
u16RequestedConsumerControl = MEDIA_VOLUME_UP;
} else {
u16RequestedConsumerControl = 0;
}
if (gu16PSoCDigitalPos & CELL_10_Msk) {
if (u32EnterPressStarted == 0xFFFFFFFF) u32EnterPressStarted = gu32NowMs;
}
}
// [Cell 11 no function]
{ // Sensitivity control (handled in dedicated function)
gaControlledIntLedData[LED_CELL_12].u8S = (gConfig.u8Sens - 1) * 16;
gaControlledIntLedData[LED_CELL_12].u8V = 255;
gaControlledIntLedData[LED_CELL_12].s = (gConfig.u8Sens - 1) * 16;
gaControlledIntLedData[LED_CELL_12].v = 255;
}
// [Cell 13 no function]
// [Cell 13,14 no function]
{ // Mode switching
gaControlledIntLedData[LED_CELL_14].u16H = gConfig.bEnableKeyboard ? GREEN : RED;
gaControlledIntLedData[LED_CELL_14].u8V = 255;
gaControlledIntLedData[LED_CELL_15].u16H = gConfig.bEnableIO4 ? GREEN : RED;
gaControlledIntLedData[LED_CELL_15].u8V = 255;
gaControlledIntLedData[LED_CELL_15].h = gConfig.bEnableKeyboard ? GREEN : RED;
gaControlledIntLedData[LED_CELL_15].v = 255;
if (gu16PSoCDigitalPos & CELL_14_Msk) INV(gConfig.bEnableKeyboard);
if (gu16PSoCDigitalPos & CELL_15_Msk) INV(gConfig.bEnableIO4);
if (gu16PSoCDigitalPos & CELL_15_Msk) INV(gConfig.bEnableKeyboard);
}
}
static inline void _FillControlled(uint16_t u16H, uint8_t u8Fill) {
for (uint8_t i = 0; i < LED_NUM_GROUND; i++) {
gaControlledIntLedData[i].u16H = u16H;
gaControlledIntLedData[i].u8S = 255;
gaControlledIntLedData[i].u8V = i < u8Fill ? 255 : 0;
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;
}
}
static uint8_t bCalibrationActive = 0;
static void UI_TickCalibration(void) {
static uint16_t u16Timer = 0;
static uint16_t su16MaxNoHands[32] = { 0 };
static uint16_t su16MaxHands[32] = { 0 };
if (!bCalibrationActive) {
// Calibration start
u16Timer = 0;
bCalibrationActive = 1;
memset(su16MaxNoHands, 0, sizeof su16MaxNoHands);
memset(su16MaxHands, 0, sizeof su16MaxHands);
}
// Only tick the timer when we're using it, to avoid overflow
if (u16Timer < 5000) u16Timer++;
if (u16Timer < 3000) {
// Flash red for the first 3 seconds
_FillControlled(RED, ((u16Timer / 250) & 1) ? 0 : LED_NUM_GROUND);
} else if (u16Timer < 4000) {
// Fill up the cells with blue
_FillControlled(BLUE, (u16Timer - 3000) / (1000 / LED_NUM_GROUND));
for (uint8_t i; i < 32; i++) {
su16MaxNoHands[i] = Maximum(su16MaxNoHands[i], gu16PSoCDiff[i]);
}
} else if (u16Timer < 5000) {
// Flash green for the next second
_FillControlled(GREEN, ((u16Timer / 250) & 1) ? 0 : LED_NUM_GROUND);
} else {
for (uint8_t i = 0; i < 32; i++) {
// As well as the raw minimum, force a small constant threshold minimum too
su16MaxHands[i] = Maximum(su16MaxHands[i], gu16PSoCDiff[i] + (gConfig.u8Sens * 5));
uint8_t u8ForceTest = 0;
if (u8TestIsActive) {
// Send a test button, to trigger entry to the test menu
if (MS_SINCE(su32EnteredTestMenuAt) < 100) {
gu16IO4ForceButtons |= IO4_BUTTON_TEST;
u8ForceTest = 1;
}
for (uint8_t i = 0; i < LED_NUM_GROUND; i++) {
gaControlledIntLedData[i].u8S = 255;
gaControlledIntLedData[i].u8V = 255;
}
uint16_t u16CalibMin = CALIB_HANDS_MIN + (gConfig.u8Sens * 50);
uint16_t u16CalibReq = CALIB_HANDS_REQ + (gConfig.u8Sens * 50);
uint16_t u16CalibMax = CALIB_HANDS_MAX + (gConfig.u8Sens * 50);
uint8_t bOk = 1;
// Iterate over the cells
for (uint8_t i = 0; i < 16; i++) {
if (su16MaxHands[i * 2] < u16CalibMin || su16MaxHands[i * 2 + 1] < u16CalibMin ||
su16MaxHands[i * 2] < su16MaxNoHands[i * 2] ||
su16MaxHands[i * 2 + 1] < su16MaxNoHands[i * 2 + 1]) {
// Not enough data
gaControlledIntLedData[i * 2].u8V = 0;
bOk = 0;
} else if (su16MaxHands[i * 2] < u16CalibReq || su16MaxHands[i * 2 + 1] < u16CalibReq) {
// Data is too low
gaControlledIntLedData[i * 2].u16H = RED;
bOk = 0;
} else if (su16MaxHands[i * 2] < u16CalibMax || su16MaxHands[i * 2 + 1] < u16CalibMax) {
// We've got enough, but it could be better
uint16_t u16Min = Minimum(su16MaxHands[i * 2], su16MaxHands[i * 2 + 1]);
gaControlledIntLedData[i * 2].u16H =
((u16Min - u16CalibReq) * GREEN) / (u16CalibMax - u16CalibReq);
// Implement the on-screen buttons
{ // Down
if (gu16PSoCDigital & (CELL_0_Msk | CELL_1_Msk | CELL_2_Msk)) {
u8V = 200;
} else {
// More than enough data
gaControlledIntLedData[i * 2].u16H = GREEN;
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;
}
for (uint8_t i = 0; i < LED_NUM_GROUND; i++) {
if (i % 2 == 1) {
gaControlledIntLedData[i].u16H = bOk ? GREEN : RED;
gaControlledIntLedData[LED_DIVIDER_2_3].v = 255;
{ // Up
if (gu16PSoCDigital & (CELL_3_Msk | CELL_4_Msk | CELL_5_Msk)) {
u8V = 200;
} 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;
}
gaControlledIntLedData[LED_DIVIDER_5_6].v = 255;
// Calibration complete
if (gu8DigitalButtons & DIGITAL_FN1_Msk) {
bCalibrationActive = 0;
if (bOk) {
memcpy(gConfig.u16PSoCScaleMin, su16MaxNoHands, sizeof su16MaxNoHands);
memcpy(gConfig.u16PSoCScaleMax, su16MaxHands, sizeof su16MaxHands);
bConfigDirty = 1;
gaControlledIntLedData[LED_DIVIDER_12_13].v = 255;
{ // OK
if (gu16PSoCDigital & (CELL_13_Msk | CELL_14_Msk | CELL_15_Msk)) {
u8V = 200;
} 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;
}
}
// Implement our custom buttons for test and service
gaControlledIntLedData[LED_DIVIDER_5_6].v = 255;
{ // Test
if (gu16PSoCDigital & (CELL_6_Msk | CELL_7_Msk)) {
u8V = 200;
gu16IO4ForceButtons |= IO4_BUTTON_TEST;
} else {
u8V = 50;
// Don't un-press the test button if we're forcing it on
if (!u8ForceTest) gu16IO4ForceButtons &= ~IO4_BUTTON_TEST;
}
gaControlledIntLedData[LED_CELL_6].v = u8V;
gaControlledIntLedData[LED_DIVIDER_6_7].v = u8V;
gaControlledIntLedData[LED_CELL_7].v = u8V;
}
gaControlledIntLedData[LED_DIVIDER_7_8].v = 255;
{ // Service
if (gu16PSoCDigital & (CELL_8_Msk | CELL_9_Msk)) {
u8V = 200;
gu16IO4ForceButtons |= IO4_BUTTON_SERVICE;
} else {
u8V = 50;
gu16IO4ForceButtons &= ~IO4_BUTTON_SERVICE;
}
gaControlledIntLedData[LED_CELL_8].v = u8V;
gaControlledIntLedData[LED_DIVIDER_8_9].v = u8V;
gaControlledIntLedData[LED_CELL_9].v = u8V;
}
gaControlledIntLedData[LED_DIVIDER_9_10].v = 255;
}
void UI_Tick(void) {
static uint8_t u8Fn2Held = 0;
if (gu8DigitalButtons & DIGITAL_FN2_Msk) {
if (u8Fn2Held < FN2_HOLD_TIME) u8Fn2Held++;
// 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 (u8Fn2Held >= FN2_HOLD_TIME) {
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;
}
u8Fn2Held = 0;
u16RequestedConsumerControl = 0;
u32EnterPressStarted = 0;
u8Fn1Held = 0;
}
static uint16_t u16Fn1Held = 0;
if (gu8DigitalButtons & DIGITAL_FN1_Msk) {
if (u16Fn1Held < FN1_HOLD_TIME) u16Fn1Held++;
// 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 (u32LastFn2 && MS_SINCE(u32LastFn2) < FN2_TAP_TIME) {
u8TestIsActive = !u8TestIsActive;
if (u8TestIsActive) {
su32EnteredTestMenuAt = gu32NowMs;
gu16IO4ForceButtons = IO4_BUTTON_TEST;
} else {
su32EnteredTestMenuAt = 0;
gu16IO4ForceButtons = 0;
}
}
u32LastFn2 = gu32NowMs;
}
u8LastDB = gu8DigitalButtons;
// Handle hold trigger on FN2
static uint16_t u16Fn2Held = 0;
if (gu8DigitalButtons & DIGITAL_FN2_Msk) {
if (u16Fn2Held < FN2_HOLD_TIME) u16Fn2Held++;
} else {
u16Fn1Held = 0;
u16Fn2Held = 0;
}
if (bCalibrationActive || u16Fn1Held >= FN1_HOLD_TIME) {
gbLedDataIsControlledInt = 1;
UI_TickCalibration();
} else if (u8Fn2Held >= FN2_HOLD_TIME) {
// Render the appropriate UI based on what's being done
if (u8Fn1Held >= FN1_HOLD_TIME) {
gbLedDataIsControlledInt = 1;
gbUIOpen = 1;
UI_TickSettings();
} else if (u16Fn2Held >= FN2_HOLD_TIME || u8TestIsActive) {
gbLedDataIsControlledInt = 1;
gbUIOpen = 0;
UI_TickServiceTest();
} else {
gbLedDataIsControlledInt = 0;
gbUIOpen = 0;
}
}

129
src/usb_def.h Normal file
View File

@ -0,0 +1,129 @@
#pragma once
#pragma once
#include <stdint.h>
#define USB_STATE_FLOATING 2
#define USB_STATE_SUSPEND 1
// USB definition files
#include "hid_def.h"
#include "usb_inc/hid.h"
#include "usb_inc/keymap.h"
#include "usb_inc/usb.h"
// Interfaces
enum : uint8_t {
USBD_ITF_CDC_CMD,
USBD_ITF_CDC_DAT,
USBD_ITF_HID_IO4,
USBD_ITF_HID_MISC,
_USBD_ITF_MAX,
};
// Endpoint number mapping
#define EP_CTRL_IN EP0
#define EP_CTRL_OUT EP1
#define EP_CDC_IN EP2
#define EP_CDC_OUT EP3
#define EP_CDC_CMD EP4
#define EP_HID_IO4_IN EP5
#define EP_HID_MISC_IN EP6
#define EP_HID_MISC_OUT EP7
#define _USBD_INTSTS(x) USBD_INTSTS_EP##x
#define USBD_INTSTS(x) _USBD_INTSTS(x)
// Must match the above!!
#define USBD_INTSTS_CTRL_IN USBD_INTSTS(EP_CTRL_IN)
#define USBD_INTSTS_CTRL_OUT USBD_INTSTS(EP_CTRL_OUT)
#define USBD_INTSTS_CDC_IN USBD_INTSTS(EP_CDC_IN)
#define USBD_INTSTS_CDC_OUT USBD_INTSTS(EP_CDC_OUT)
#define USBD_INTSTS_CDC_CMD USBD_INTSTS(EP_CDC_CMD)
#define USBD_INTSTS_HID_IO4_IN USBD_INTSTS(EP_HID_IO4_IN)
#define USBD_INTSTS_HID_MISC_IN USBD_INTSTS(EP_HID_MISC_IN)
#define USBD_INTSTS_HID_MISC_OUT USBD_INTSTS(EP_HID_MISC_OUT)
#define USBD_CDC_EP_IN (1 | EP_INPUT)
#define USBD_CDC_EP_OUT (2 | EP_OUTPUT)
#define USBD_CDC_EP_CMD (3 | EP_INPUT)
#define USBD_HID_IO4_EP_IN (4 | EP_INPUT)
#define USBD_HID_MISC_EP_IN (5 | EP_INPUT)
#define USBD_HID_MISC_EP_OUT (6 | EP_OUTPUT)
#define USBD_SETUP_BUF_LEN (8)
#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)
_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),
"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
#define SETUP_BUF_BASE 0
#define SETUP_BUF_LEN 8
_Static_assert((SETUP_BUF_LEN + EP0_MAX_PKT_SIZE + EP1_MAX_PKT_SIZE + EP2_MAX_PKT_SIZE +
EP3_MAX_PKT_SIZE + EP4_MAX_PKT_SIZE + EP5_MAX_PKT_SIZE + EP6_MAX_PKT_SIZE +
EP7_MAX_PKT_SIZE) <= 512,
"USB endpoint packet sizes exceeds 512-byte maximum");
#define EP0_BUF_BASE (SETUP_BUF_BASE + SETUP_BUF_LEN)
#define EP1_BUF_BASE (EP0_BUF_BASE + EP0_MAX_PKT_SIZE)
#define EP2_BUF_BASE (EP1_BUF_BASE + EP1_MAX_PKT_SIZE)
#define EP3_BUF_BASE (EP2_BUF_BASE + EP2_MAX_PKT_SIZE)
#define EP4_BUF_BASE (EP3_BUF_BASE + EP3_MAX_PKT_SIZE)
#define EP5_BUF_BASE (EP4_BUF_BASE + EP4_MAX_PKT_SIZE)
#define EP6_BUF_BASE (EP5_BUF_BASE + EP5_MAX_PKT_SIZE)
#define EP7_BUF_BASE (EP5_BUF_BASE + EP6_MAX_PKT_SIZE)
// Define Descriptor information
#define HID_IO4_INT_IN_INTERVAL 8
#define HID_DEFAULT_INT_IN_INTERVAL 1
#define HID_DEFAULT_INT_OUT_INTERVAL 1
#define USBD_SELF_POWERED_Pos 6
#define USBD_SELF_POWERED_Msk (1 << USBD_SELF_POWERED_Pos)
#define USBD_REMOTE_WAKEUP_Pos 5
#define USBD_REMOTE_WAKEUP_Msk (1 << USBD_REMOTE_WAKEUP_Pos)
#define USBD_MAX_POWER (500 / 2)
// Endpoint handler functions (usbd_user.c)
void EP_CDC_OUT_Handler(void);
void EP_CDC_IN_Handler(void);
void EP_HID_IO4_IN_Handler(void);
void EP_HID_MISC_IN_Handler(void);
void EP_HID_MISC_OUT_Handler(void);
// Custom USBD implementation (usbd_driver.c)
void Tas_USBD_Open(void);
void Tas_USBD_Init(void);
void Tas_USBD_Start(void);
void Tas_USBD_ClassRequest(void);
void Tas_USBD_GetSetupPacket(usb_setup_t *buf);
void Tas_USBD_ProcessSetupPacket(void);
void Tas_USBD_PrepareCtrlIn(void *pu8Buf, uint32_t u32Size);
void Tas_USBD_CtrlIn(void);
void Tas_USBD_PrepareCtrlOut(void *pu8Buf, uint32_t u32Size,
void (*pCallback)(volatile uint8_t *, uint32_t));
void Tas_USBD_CtrlOut(void);
void Tas_USBD_SwReset(void);
extern volatile uint8_t *g_usbd_CtrlInPointer;
extern volatile uint32_t g_usbd_CtrlInSize;
// General USB control
extern volatile uint8_t g_u8UsbState;
extern uint8_t g_u8Idle;
extern uint8_t g_u8Protocol;

View File

@ -622,22 +622,8 @@ static const uint16_t _asciimap[] = {
// The following characters belong to ISO-8859-15
// ! The first 16 values here are used for the numpad
KEYPAD_0,
KEYPAD_1,
KEYPAD_2,
KEYPAD_3,
KEYPAD_4,
KEYPAD_5,
KEYPAD_6,
KEYPAD_7,
KEYPAD_8,
KEYPAD_9,
KEYPAD_ADD,
KEYPAD_SUBTRACT,
KEYPAD_MULTIPLY,
KEYPAD_DIVIDE,
KEYPAD_ENTER,
KEYPAD_DOT,
KEYPAD_0, KEYPAD_1, KEYPAD_2, KEYPAD_3, KEYPAD_4, KEYPAD_5, KEYPAD_6, KEYPAD_7, KEYPAD_8,
KEYPAD_9, KEYPAD_ADD, KEYPAD_SUBTRACT, KEYPAD_MULTIPLY, KEYPAD_DIVIDE, KEYPAD_ENTER, KEYPAD_DOT,
// KEY_RESERVED, // 128 - Unused
// KEY_RESERVED, // 129 - Unused
// KEY_RESERVED, // 130 - Unused
@ -767,3 +753,425 @@ static const uint16_t _asciimap[] = {
KEY_RESERVED, // 254 - Thorn
KEY_RESERVED, // 255 - 'y' Umlaut
};
enum ConsumerKeycode : uint16_t {
// Some keys might only work with linux
CONSUMER_POWER = 0x30,
CONSUMER_SLEEP = 0x32,
MEDIA_RECORD = 0xB2,
MEDIA_FAST_FORWARD = 0xB3,
MEDIA_REWIND = 0xB4,
MEDIA_NEXT = 0xB5,
MEDIA_PREVIOUS = 0xB6,
MEDIA_PREV = 0xB6, // Alias
MEDIA_STOP = 0xB7,
MEDIA_PLAY_PAUSE = 0xCD,
MEDIA_PAUSE = 0xB0,
MEDIA_VOLUME_MUTE = 0xE2,
MEDIA_VOL_MUTE = 0xE2, // Alias
MEDIA_VOLUME_UP = 0xE9,
MEDIA_VOL_UP = 0xE9, // Alias
MEDIA_VOLUME_DOWN = 0xEA,
MEDIA_VOL_DOWN = 0xEA, // Alias
CONSUMER_BRIGHTNESS_UP = 0x006F,
CONSUMER_BRIGHTNESS_DOWN = 0x0070,
CONSUMER_SCREENSAVER = 0x19e,
CONSUMER_PROGRAMMABLE_BUTTON_CONFIGURATION = 0x182,
CONSUMER_CONTROL_CONFIGURATION = 0x183,
CONSUMER_EMAIL_READER = 0x18A,
CONSUMER_CALCULATOR = 0x192,
CONSUMER_EXPLORER = 0x194,
CONSUMER_BROWSER_HOME = 0x223,
CONSUMER_BROWSER_BACK = 0x224,
CONSUMER_BROWSER_FORWARD = 0x225,
CONSUMER_BROWSER_REFRESH = 0x227,
CONSUMER_BROWSER_BOOKMARKS = 0x22A,
// Consumer_Page_(0x0C) 0x15
HID_CONSUMER_UNASSIGNED = 0x00,
HID_CONSUMER_NUMERIC_KEY_PAD = 0x02, // HID type NARY
HID_CONSUMER_PROGRAMMABLE_BUTTONS = 0x03, // HID type NARY
HID_CONSUMER_MICROPHONE_CA = 0x04,
HID_CONSUMER_HEADPHONE_CA = 0x05,
HID_CONSUMER_GRAPHIC_EQUALIZER_CA = 0x06,
// Reserved = 0x07-1F
HID_CONSUMER_PLUS_10 = 0x20, // HID type OSC
HID_CONSUMER_PLUS_100 = 0x21, // HID type OSC
HID_CONSUMER_AM_SLASH_PM = 0x22, // HID type OSC
// Reserved = 0x23-3F
HID_CONSUMER_POWER = 0x30, // HID type OOC
HID_CONSUMER_RESET = 0x31, // HID type OSC
HID_CONSUMER_SLEEP = 0x32, // HID type OSC
HID_CONSUMER_SLEEP_AFTER = 0x33, // HID type OSC
HID_CONSUMER_SLEEP_MODE = 0x34, // HID type RTC
HID_CONSUMER_ILLUMINATION = 0x35, // HID type OOC
HID_CONSUMER_FUNCTION_BUTTONS = 0x36, // HID type NARY
// Reserved = 0x37-3F
HID_CONSUMER_MENU = 0x40, // HID type OOC
HID_CONSUMER_MENU_PICK = 0x41, // HID type OSC
HID_CONSUMER_MENU_UP = 0x42, // HID type OSC
HID_CONSUMER_MENU_DOWN = 0x43, // HID type OSC
HID_CONSUMER_MENU_LEFT = 0x44, // HID type OSC
HID_CONSUMER_MENU_RIGHT = 0x45, // HID type OSC
HID_CONSUMER_MENU_ESCAPE = 0x46, // HID type OSC
HID_CONSUMER_MENU_VALUE_INCREASE = 0x47, // HID type OSC
HID_CONSUMER_MENU_VALUE_DECREASE = 0x48, // HID type OSC
// Reserved 0x49-5F
HID_CONSUMER_DATA_ON_SCREEN = 0x60, // HID type OOC
HID_CONSUMER_CLOSED_CAPTION = 0x61, // HID type OOC
HID_CONSUMER_CLOSED_CAPTION_SELECT = 0x62, // HID type OSC
HID_CONSUMER_VCR_SLASH_TV = 0x63, // HID type OOC
HID_CONSUMER_BROADCAST_MODE = 0x64, // HID type OSC
HID_CONSUMER_SNAPSHOT = 0x65, // HID type OSC
HID_CONSUMER_STILL = 0x66, // HID type OSC
// Reserved 0x67-7F
HID_CONSUMER_SELECTION = 0x80, // HID type NARY
HID_CONSUMER_ASSIGN_SELECTION = 0x81, // HID type OSC
HID_CONSUMER_MODE_STEP = 0x82, // HID type OSC
HID_CONSUMER_RECALL_LAST = 0x83, // HID type OSC
HID_CONSUMER_ENTER_CHANNEL = 0x84, // HID type OSC
HID_CONSUMER_ORDER_MOVIE = 0x85, // HID type OSC
HID_CONSUMER_CHANNEL = 0x86, // HID type LC
HID_CONSUMER_MEDIA_SELECTION = 0x87, // HID type NARY
HID_CONSUMER_MEDIA_SELECT_COMPUTER = 0x88, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_TV = 0x89, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_WWW = 0x8A, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_DVD = 0x8B, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_TELEPHONE = 0x8C, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_PROGRAM_GUIDE = 0x8D, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_VIDEO_PHONE = 0x8E, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_GAMES = 0x8F, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_MESSAGES = 0x90, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_CD = 0x91, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_VCR = 0x92, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_TUNER = 0x93, // HID type SEL
HID_CONSUMER_QUIT = 0x94, // HID type OSC
HID_CONSUMER_HELP = 0x95, // HID type OOC
HID_CONSUMER_MEDIA_SELECT_TAPE = 0x96, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_CABLE = 0x97, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_SATELLITE = 0x98, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_SECURITY = 0x99, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_HOME = 0x9A, // HID type SEL
HID_CONSUMER_MEDIA_SELECT_CALL = 0x9B, // HID type SEL
HID_CONSUMER_CHANNEL_INCREMENT = 0x9C, // HID type OSC
HID_CONSUMER_CHANNEL_DECREMENT = 0x9D, // HID type OSC
HID_CONSUMER_MEDIA_SELECT_SAP = 0x9E, // HID type SEL
// Reserved 0x9F
HID_CONSUMER_VCR_PLUS = 0xA0, // HID type OSC
HID_CONSUMER_ONCE = 0xA1, // HID type OSC
HID_CONSUMER_DAILY = 0xA2, // HID type OSC
HID_CONSUMER_WEEKLY = 0xA3, // HID type OSC
HID_CONSUMER_MONTHLY = 0xA4, // HID type OSC
// Reserved 0xA5-AF
HID_CONSUMER_PLAY = 0xB0, // HID type OOC
HID_CONSUMER_PAUSE = 0xB1, // HID type OOC
HID_CONSUMER_RECORD = 0xB2, // HID type OOC
HID_CONSUMER_FAST_FORWARD = 0xB3, // HID type OOC
HID_CONSUMER_REWIND = 0xB4, // HID type OOC
HID_CONSUMER_SCAN_NEXT_TRACK = 0xB5, // HID type OSC
HID_CONSUMER_SCAN_PREVIOUS_TRACK = 0xB6, // HID type OSC
HID_CONSUMER_STOP = 0xB7, // HID type OSC
HID_CONSUMER_EJECT = 0xB8, // HID type OSC
HID_CONSUMER_RANDOM_PLAY = 0xB9, // HID type OOC
HID_CONSUMER_SELECT_DISC = 0xBA, // HID type NARY
HID_CONSUMER_ENTER_DISC_MC = 0xBB,
HID_CONSUMER_REPEAT = 0xBC, // HID type OSC
HID_CONSUMER_TRACKING = 0xBD, // HID type LC
HID_CONSUMER_TRACK_NORMAL = 0xBE, // HID type OSC
HID_CONSUMER_SLOW_TRACKING = 0xBF, // HID type LC
HID_CONSUMER_FRAME_FORWARD = 0xC0, // HID type RTC
HID_CONSUMER_FRAME_BACK = 0xC1, // HID type RTC
HID_CONSUMER_MARK = 0xC2, // HID type OSC
HID_CONSUMER_CLEAR_MARK = 0xC3, // HID type OSC
HID_CONSUMER_REPEAT_FROM_MARK = 0xC4, // HID type OOC
HID_CONSUMER_RETURN_TO_MARK = 0xC5, // HID type OSC
HID_CONSUMER_SEARCH_MARK_FORWARD = 0xC6, // HID type OSC
HID_CONSUMER_SEARCH_MARK_BACKWARDS = 0xC7, // HID type OSC
HID_CONSUMER_COUNTER_RESET = 0xC8, // HID type OSC
HID_CONSUMER_SHOW_COUNTER = 0xC9, // HID type OSC
HID_CONSUMER_TRACKING_INCREMENT = 0xCA, // HID type RTC
HID_CONSUMER_TRACKING_DECREMENT = 0xCB, // HID type RTC
HID_CONSUMER_STOP_SLASH_EJECT = 0xCC, // HID type OSC
HID_CONSUMER_PLAY_SLASH_PAUSE = 0xCD, // HID type OSC
HID_CONSUMER_PLAY_SLASH_SKIP = 0xCE, // HID type OSC
// Reserved 0xCF-DF
HID_CONSUMER_VOLUME = 0xE0, // HID type LC
HID_CONSUMER_BALANCE = 0xE1, // HID type LC
HID_CONSUMER_MUTE = 0xE2, // HID type OOC
HID_CONSUMER_BASS = 0xE3, // HID type LC
HID_CONSUMER_TREBLE = 0xE4, // HID type LC
HID_CONSUMER_BASS_BOOST = 0xE5, // HID type OOC
HID_CONSUMER_SURROUND_MODE = 0xE6, // HID type OSC
HID_CONSUMER_LOUDNESS = 0xE7, // HID type OOC
HID_CONSUMER_MPX = 0xE8, // HID type OOC
HID_CONSUMER_VOLUME_INCREMENT = 0xE9, // HID type RTC
HID_CONSUMER_VOLUME_DECREMENT = 0xEA, // HID type RTC
// Reserved 0xEB-EF
HID_CONSUMER_SPEED_SELECT = 0xF0, // HID type OSC
HID_CONSUMER_PLAYBACK_SPEED = 0xF1, // HID type NARY
HID_CONSUMER_STANDARD_PLAY = 0xF2, // HID type SEL
HID_CONSUMER_LONG_PLAY = 0xF3, // HID type SEL
HID_CONSUMER_EXTENDED_PLAY = 0xF4, // HID type SEL
HID_CONSUMER_SLOW = 0xF5, // HID type OSC
// Reserved 0xF6-FF
HID_CONSUMER_FAN_ENABLE = 0x100, // HID type OOC
HID_CONSUMER_FAN_SPEED = 0x101, // HID type LC
HID_CONSUMER_LIGHT_ENABLE = 0x102, // HID type OOC
HID_CONSUMER_LIGHT_ILLUMINATION_LEVEL = 0x103, // HID type LC
HID_CONSUMER_CLIMATE_CONTROL_ENABLE = 0x104, // HID type OOC
HID_CONSUMER_ROOM_TEMPERATURE = 0x105, // HID type LC
HID_CONSUMER_SECURITY_ENABLE = 0x106, // HID type OOC
HID_CONSUMER_FIRE_ALARM = 0x107, // HID type OSC
HID_CONSUMER_POLICE_ALARM = 0x108, // HID type OSC
HID_CONSUMER_PROXIMITY = 0x109, // HID type LC
HID_CONSUMER_MOTION = 0x10A, // HID type OSC
HID_CONSUMER_DURESS_ALARM = 0x10B, // HID type OSC
HID_CONSUMER_HOLDUP_ALARM = 0x10C, // HID type OSC
HID_CONSUMER_MEDICAL_ALARM = 0x10D, // HID type OSC
// Reserved 0x10E-14F
HID_CONSUMER_BALANCE_RIGHT = 0x150, // HID type RTC
HID_CONSUMER_BALANCE_LEFT = 0x151, // HID type RTC
HID_CONSUMER_BASS_INCREMENT = 0x152, // HID type RTC
HID_CONSUMER_BASS_DECREMENT = 0x153, // HID type RTC
HID_CONSUMER_TREBLE_INCREMENT = 0x154, // HID type RTC
HID_CONSUMER_TREBLE_DECREMENT = 0x155, // HID type RTC
// Reserved 0x156-15F
HID_CONSUMER_SPEAKER_SYSTEM = 0x160, // HID type CL
HID_CONSUMER_CHANNEL_LEFT = 0x161, // HID type CL
HID_CONSUMER_CHANNEL_RIGHT = 0x162, // HID type CL
HID_CONSUMER_CHANNEL_CENTER = 0x163, // HID type CL
HID_CONSUMER_CHANNEL_FRONT = 0x164, // HID type CL
HID_CONSUMER_CHANNEL_CENTER_FRONT = 0x165, // HID type CL
HID_CONSUMER_CHANNEL_SIDE = 0x166, // HID type CL
HID_CONSUMER_CHANNEL_SURROUND = 0x167, // HID type CL
HID_CONSUMER_CHANNEL_LOW_FREQUENCY_ENHANCEMENT = 0x168, // HID type CL
HID_CONSUMER_CHANNEL_TOP = 0x169, // HID type CL
HID_CONSUMER_CHANNEL_UNKNOWN = 0x16A, // HID type CL
// Reserved 0x16B-16F
HID_CONSUMER_SUB_CHANNEL = 0x170, // HID type LC
HID_CONSUMER_SUB_CHANNEL_INCREMENT = 0x171, // HID type OSC
HID_CONSUMER_SUB_CHANNEL_DECREMENT = 0x172, // HID type OSC
HID_CONSUMER_ALTERNATE_AUDIO_INCREMENT = 0x173, // HID type OSC
HID_CONSUMER_ALTERNATE_AUDIO_DECREMENT = 0x174, // HID type OSC
// Reserved 0x175-17F
HID_CONSUMER_APPLICATION_LAUNCH_BUTTONS = 0x180, // HID type NARY
HID_CONSUMER_AL_LAUNCH_BUTTON_CONFIGURATION_TOOL = 0x181, // HID type SEL
HID_CONSUMER_AL_PROGRAMMABLE_BUTTON_CONFIGURATION = 0x182, // HID type SEL
HID_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x183, // HID type SEL
HID_CONSUMER_AL_WORD_PROCESSOR = 0x184, // HID type SEL
HID_CONSUMER_AL_TEXT_EDITOR = 0x185, // HID type SEL
HID_CONSUMER_AL_SPREADSHEET = 0x186, // HID type SEL
HID_CONSUMER_AL_GRAPHICS_EDITOR = 0x187, // HID type SEL
HID_CONSUMER_AL_PRESENTATION_APP = 0x188, // HID type SEL
HID_CONSUMER_AL_DATABASE_APP = 0x189, // HID type SEL
HID_CONSUMER_AL_EMAIL_READER = 0x18A, // HID type SEL
HID_CONSUMER_AL_NEWSREADER = 0x18B, // HID type SEL
HID_CONSUMER_AL_VOICEMAIL = 0x18C, // HID type SEL
HID_CONSUMER_AL_CONTACTS_SLASH_ADDRESS_BOOK = 0x18D, // HID type SEL
HID_CONSUMER_AL_CALENDAR_SLASH_SCHEDULE = 0x18E, // HID type SEL
HID_CONSUMER_AL_TASK_SLASH_PROJECT_MANAGER = 0x18F, // HID type SEL
HID_CONSUMER_AL_LOG_SLASH_JOURNAL_SLASH_TIMECARD = 0x190, // HID type SEL
HID_CONSUMER_AL_CHECKBOOK_SLASH_FINANCE = 0x191, // HID type SEL
HID_CONSUMER_AL_CALCULATOR = 0x192, // HID type SEL
HID_CONSUMER_AL_A_SLASH_V_CAPTURE_SLASH_PLAYBACK = 0x193, // HID type SEL
HID_CONSUMER_AL_LOCAL_MACHINE_BROWSER = 0x194, // HID type SEL
HID_CONSUMER_AL_LAN_SLASH_WAN_BROWSER = 0x195, // HID type SEL
HID_CONSUMER_AL_INTERNET_BROWSER = 0x196, // HID type SEL
HID_CONSUMER_AL_REMOTE_NETWORKING_SLASH_ISP_CONNECT = 0x197, // HID type SEL
HID_CONSUMER_AL_NETWORK_CONFERENCE = 0x198, // HID type SEL
HID_CONSUMER_AL_NETWORK_CHAT = 0x199, // HID type SEL
HID_CONSUMER_AL_TELEPHONY_SLASH_DIALER = 0x19A, // HID type SEL
HID_CONSUMER_AL_LOGON = 0x19B, // HID type SEL
HID_CONSUMER_AL_LOGOFF = 0x19C, // HID type SEL
HID_CONSUMER_AL_LOGON_SLASH_LOGOFF = 0x19D, // HID type SEL
HID_CONSUMER_AL_TERMINAL_LOCK_SLASH_SCREENSAVER = 0x19E, // HID type SEL
HID_CONSUMER_AL_CONTROL_PANEL = 0x19F, // HID type SEL
HID_CONSUMER_AL_COMMAND_LINE_PROCESSOR_SLASH_RUN = 0x1A0, // HID type SEL
HID_CONSUMER_AL_PROCESS_SLASH_TASK_MANAGER = 0x1A1, // HID type SEL
HID_CONSUMER_AL_SELECT_TASK_SLASH_APPLICATION = 0x1A2, // HID type SEL
HID_CONSUMER_AL_NEXT_TASK_SLASH_APPLICATION = 0x1A3, // HID type SEL
HID_CONSUMER_AL_PREVIOUS_TASK_SLASH_APPLICATION = 0x1A4, // HID type SEL
HID_CONSUMER_AL_PREEMPTIVE_HALT_TASK_SLASH_APPLICATION = 0x1A5, // HID type SEL
HID_CONSUMER_AL_INTEGRATED_HELP_CENTER = 0x1A6, // HID type SEL
HID_CONSUMER_AL_DOCUMENTS = 0x1A7, // HID type SEL
HID_CONSUMER_AL_THESAURUS = 0x1A8, // HID type SEL
HID_CONSUMER_AL_DICTIONARY = 0x1A9, // HID type SEL
HID_CONSUMER_AL_DESKTOP = 0x1AA, // HID type SEL
HID_CONSUMER_AL_SPELL_CHECK = 0x1AB, // HID type SEL
HID_CONSUMER_AL_GRAMMAR_CHECK = 0x1AC, // HID type SEL
HID_CONSUMER_AL_WIRELESS_STATUS = 0x1AD, // HID type SEL
HID_CONSUMER_AL_KEYBOARD_LAYOUT = 0x1AE, // HID type SEL
HID_CONSUMER_AL_VIRUS_PROTECTION = 0x1AF, // HID type SEL
HID_CONSUMER_AL_ENCRYPTION = 0x1B0, // HID type SEL
HID_CONSUMER_AL_SCREEN_SAVER = 0x1B1, // HID type SEL
HID_CONSUMER_AL_ALARMS = 0x1B2, // HID type SEL
HID_CONSUMER_AL_CLOCK = 0x1B3, // HID type SEL
HID_CONSUMER_AL_FILE_BROWSER = 0x1B4, // HID type SEL
HID_CONSUMER_AL_POWER_STATUS = 0x1B5, // HID type SEL
HID_CONSUMER_AL_IMAGE_BROWSER = 0x1B6, // HID type SEL
HID_CONSUMER_AL_AUDIO_BROWSER = 0x1B7, // HID type SEL
HID_CONSUMER_AL_MOVIE_BROWSER = 0x1B8, // HID type SEL
HID_CONSUMER_AL_DIGITAL_RIGHTS_MANAGER = 0x1B9, // HID type SEL
HID_CONSUMER_AL_DIGITAL_WALLET = 0x1BA, // HID type SEL
// _Reserved 0x1BB
HID_CONSUMER_AL_INSTANT_MESSAGING = 0x1BC, // HID type SEL
HID_CONSUMER_AL_OEM_FEATURES_SLASH__TIPS_SLASH_TUTORIAL_BROWSER = 0x1BD, // HID type SEL
HID_CONSUMER_AL_OEM_HELP = 0x1BE, // HID type SEL
HID_CONSUMER_AL_ONLINE_COMMUNITY = 0x1BF, // HID type SEL
HID_CONSUMER_AL_ENTERTAINMENT_CONTENT_BROWSER = 0x1C0, // HID type SEL
HID_CONSUMER_AL_ONLINE_SHOPPING_BROWSER = 0x1C1, // HID type SEL
HID_CONSUMER_AL_SMARTCARD_INFORMATION_SLASH_HELP = 0x1C2, // HID type SEL
HID_CONSUMER_AL_MARKET_MONITOR_SLASH_FINANCE_BROWSER = 0x1C3, // HID type SEL
HID_CONSUMER_AL_CUSTOMIZED_CORPORATE_NEWS_BROWSER = 0x1C4, // HID type SEL
HID_CONSUMER_AL_ONLINE_ACTIVITY_BROWSER = 0x1C5, // HID type SEL
HID_CONSUMER_AL_RESEARCH_SLASH_SEARCH_BROWSER = 0x1C6, // HID type SEL
HID_CONSUMER_AL_AUDIO_PLAYER = 0x1C7, // HID type SEL
// Reserved 0x1C8-1FF
HID_CONSUMER_GENERIC_GUI_APPLICATION_CONTROLS = 0x200, // HID type NARY
HID_CONSUMER_AC_NEW = 0x201, // HID type SEL
HID_CONSUMER_AC_OPEN = 0x202, // HID type SEL
HID_CONSUMER_AC_CLOSE = 0x203, // HID type SEL
HID_CONSUMER_AC_EXIT = 0x204, // HID type SEL
HID_CONSUMER_AC_MAXIMIZE = 0x205, // HID type SEL
HID_CONSUMER_AC_MINIMIZE = 0x206, // HID type SEL
HID_CONSUMER_AC_SAVE = 0x207, // HID type SEL
HID_CONSUMER_AC_PRINT = 0x208, // HID type SEL
HID_CONSUMER_AC_PROPERTIES = 0x209, // HID type SEL
HID_CONSUMER_AC_UNDO = 0x21A, // HID type SEL
HID_CONSUMER_AC_COPY = 0x21B, // HID type SEL
HID_CONSUMER_AC_CUT = 0x21C, // HID type SEL
HID_CONSUMER_AC_PASTE = 0x21D, // HID type SEL
HID_CONSUMER_AC_SELECT_ALL = 0x21E, // HID type SEL
HID_CONSUMER_AC_FIND = 0x21F, // HID type SEL
HID_CONSUMER_AC_FIND_AND_REPLACE = 0x220, // HID type SEL
HID_CONSUMER_AC_SEARCH = 0x221, // HID type SEL
HID_CONSUMER_AC_GO_TO = 0x222, // HID type SEL
HID_CONSUMER_AC_HOME = 0x223, // HID type SEL
HID_CONSUMER_AC_BACK = 0x224, // HID type SEL
HID_CONSUMER_AC_FORWARD = 0x225, // HID type SEL
HID_CONSUMER_AC_STOP = 0x226, // HID type SEL
HID_CONSUMER_AC_REFRESH = 0x227, // HID type SEL
HID_CONSUMER_AC_PREVIOUS_LINK = 0x228, // HID type SEL
HID_CONSUMER_AC_NEXT_LINK = 0x229, // HID type SEL
HID_CONSUMER_AC_BOOKMARKS = 0x22A, // HID type SEL
HID_CONSUMER_AC_HISTORY = 0x22B, // HID type SEL
HID_CONSUMER_AC_SUBSCRIPTIONS = 0x22C, // HID type SEL
HID_CONSUMER_AC_ZOOM_IN = 0x22D, // HID type SEL
HID_CONSUMER_AC_ZOOM_OUT = 0x22E, // HID type SEL
HID_CONSUMER_AC_ZOOM = 0x22F, // HID type LC
HID_CONSUMER_AC_FULL_SCREEN_VIEW = 0x230, // HID type SEL
HID_CONSUMER_AC_NORMAL_VIEW = 0x231, // HID type SEL
HID_CONSUMER_AC_VIEW_TOGGLE = 0x232, // HID type SEL
HID_CONSUMER_AC_SCROLL_UP = 0x233, // HID type SEL
HID_CONSUMER_AC_SCROLL_DOWN = 0x234, // HID type SEL
HID_CONSUMER_AC_SCROLL = 0x235, // HID type LC
HID_CONSUMER_AC_PAN_LEFT = 0x236, // HID type SEL
HID_CONSUMER_AC_PAN_RIGHT = 0x237, // HID type SEL
HID_CONSUMER_AC_PAN = 0x238, // HID type LC
HID_CONSUMER_AC_NEW_WINDOW = 0x239, // HID type SEL
HID_CONSUMER_AC_TILE_HORIZONTALLY = 0x23A, // HID type SEL
HID_CONSUMER_AC_TILE_VERTICALLY = 0x23B, // HID type SEL
HID_CONSUMER_AC_FORMAT = 0x23C, // HID type SEL
HID_CONSUMER_AC_EDIT = 0x23D, // HID type SEL
HID_CONSUMER_AC_BOLD = 0x23E, // HID type SEL
HID_CONSUMER_AC_ITALICS = 0x23F, // HID type SEL
HID_CONSUMER_AC_UNDERLINE = 0x240, // HID type SEL
HID_CONSUMER_AC_STRIKETHROUGH = 0x241, // HID type SEL
HID_CONSUMER_AC_SUBSCRIPT = 0x242, // HID type SEL
HID_CONSUMER_AC_SUPERSCRIPT = 0x243, // HID type SEL
HID_CONSUMER_AC_ALL_CAPS = 0x244, // HID type SEL
HID_CONSUMER_AC_ROTATE = 0x245, // HID type SEL
HID_CONSUMER_AC_RESIZE = 0x246, // HID type SEL
HID_CONSUMER_AC_FLIP_HORIZONTAL = 0x247, // HID type SEL
HID_CONSUMER_AC_FLIP_VERTICAL = 0x248, // HID type SEL
HID_CONSUMER_AC_MIRROR_HORIZONTAL = 0x249, // HID type SEL
HID_CONSUMER_AC_MIRROR_VERTICAL = 0x24A, // HID type SEL
HID_CONSUMER_AC_FONT_SELECT = 0x24B, // HID type SEL
HID_CONSUMER_AC_FONT_COLOR = 0x24C, // HID type SEL
HID_CONSUMER_AC_FONT_SIZE = 0x24D, // HID type SEL
HID_CONSUMER_AC_JUSTIFY_LEFT = 0x24E, // HID type SEL
HID_CONSUMER_AC_JUSTIFY_CENTER_H = 0x24F, // HID type SEL
HID_CONSUMER_AC_JUSTIFY_RIGHT = 0x250, // HID type SEL
HID_CONSUMER_AC_JUSTIFY_BLOCK_H = 0x251, // HID type SEL
HID_CONSUMER_AC_JUSTIFY_TOP = 0x252, // HID type SEL
HID_CONSUMER_AC_JUSTIFY_CENTER_V = 0x253, // HID type SEL
HID_CONSUMER_AC_JUSTIFY_BOTTOM = 0x254, // HID type SEL
HID_CONSUMER_AC_JUSTIFY_BLOCK_V = 0x255, // HID type SEL
HID_CONSUMER_AC_INDENT_DECREASE = 0x256, // HID type SEL
HID_CONSUMER_AC_INDENT_INCREASE = 0x257, // HID type SEL
HID_CONSUMER_AC_NUMBERED_LIST = 0x258, // HID type SEL
HID_CONSUMER_AC_RESTART_NUMBERING = 0x259, // HID type SEL
HID_CONSUMER_AC_BULLETED_LIST = 0x25A, // HID type SEL
HID_CONSUMER_AC_PROMOTE = 0x25B, // HID type SEL
HID_CONSUMER_AC_DEMOTE = 0x25C, // HID type SEL
HID_CONSUMER_AC_YES = 0x25D, // HID type SEL
HID_CONSUMER_AC_NO = 0x25E, // HID type SEL
HID_CONSUMER_AC_CANCEL = 0x25F, // HID type SEL
HID_CONSUMER_AC_CATALOG = 0x260, // HID type SEL
HID_CONSUMER_AC_BUY_SLASH_CHECKOUT = 0x261, // HID type SEL
HID_CONSUMER_AC_ADD_TO_CART = 0x262, // HID type SEL
HID_CONSUMER_AC_EXPAND = 0x263, // HID type SEL
HID_CONSUMER_AC_EXPAND_ALL = 0x264, // HID type SEL
HID_CONSUMER_AC_COLLAPSE = 0x265, // HID type SEL
HID_CONSUMER_AC_COLLAPSE_ALL = 0x266, // HID type SEL
HID_CONSUMER_AC_PRINT_PREVIEW = 0x267, // HID type SEL
HID_CONSUMER_AC_PASTE_SPECIAL = 0x268, // HID type SEL
HID_CONSUMER_AC_INSERT_MODE = 0x269, // HID type SEL
HID_CONSUMER_AC_DELETE = 0x26A, // HID type SEL
HID_CONSUMER_AC_LOCK = 0x26B, // HID type SEL
HID_CONSUMER_AC_UNLOCK = 0x26C, // HID type SEL
HID_CONSUMER_AC_PROTECT = 0x26D, // HID type SEL
HID_CONSUMER_AC_UNPROTECT = 0x26E, // HID type SEL
HID_CONSUMER_AC_ATTACH_COMMENT = 0x26F, // HID type SEL
HID_CONSUMER_AC_DELETE_COMMENT = 0x270, // HID type SEL
HID_CONSUMER_AC_VIEW_COMMENT = 0x271, // HID type SEL
HID_CONSUMER_AC_SELECT_WORD = 0x272, // HID type SEL
HID_CONSUMER_AC_SELECT_SENTENCE = 0x273, // HID type SEL
HID_CONSUMER_AC_SELECT_PARAGRAPH = 0x274, // HID type SEL
HID_CONSUMER_AC_SELECT_COLUMN = 0x275, // HID type SEL
HID_CONSUMER_AC_SELECT_ROW = 0x276, // HID type SEL
HID_CONSUMER_AC_SELECT_TABLE = 0x277, // HID type SEL
HID_CONSUMER_AC_SELECT_OBJECT = 0x278, // HID type SEL
HID_CONSUMER_AC_REDO_SLASH_REPEAT = 0x279, // HID type SEL
HID_CONSUMER_AC_SORT = 0x27A, // HID type SEL
HID_CONSUMER_AC_SORT_ASCENDING = 0x27B, // HID type SEL
HID_CONSUMER_AC_SORT_DESCENDING = 0x27C, // HID type SEL
HID_CONSUMER_AC_FILTER = 0x27D, // HID type SEL
HID_CONSUMER_AC_SET_CLOCK = 0x27E, // HID type SEL
HID_CONSUMER_AC_VIEW_CLOCK = 0x27F, // HID type SEL
HID_CONSUMER_AC_SELECT_TIME_ZONE = 0x280, // HID type SEL
HID_CONSUMER_AC_EDIT_TIME_ZONES = 0x281, // HID type SEL
HID_CONSUMER_AC_SET_ALARM = 0x282, // HID type SEL
HID_CONSUMER_AC_CLEAR_ALARM = 0x283, // HID type SEL
HID_CONSUMER_AC_SNOOZE_ALARM = 0x284, // HID type SEL
HID_CONSUMER_AC_RESET_ALARM = 0x285, // HID type SEL
HID_CONSUMER_AC_SYNCHRONIZE = 0x286, // HID type SEL
HID_CONSUMER_AC_SEND_SLASH_RECEIVE = 0x287, // HID type SEL
HID_CONSUMER_AC_SEND_TO = 0x288, // HID type SEL
HID_CONSUMER_AC_REPLY = 0x289, // HID type SEL
HID_CONSUMER_AC_REPLY_ALL = 0x28A, // HID type SEL
HID_CONSUMER_AC_FORWARD_MSG = 0x28B, // HID type SEL
HID_CONSUMER_AC_SEND = 0x28C, // HID type SEL
HID_CONSUMER_AC_ATTACH_FILE = 0x28D, // HID type SEL
HID_CONSUMER_AC_UPLOAD = 0x28E, // HID type SEL
HID_CONSUMER_AC_DOWNLOAD_SAVE_TARGET_AS = 0x28F, // HID type SEL
HID_CONSUMER_AC_SET_BORDERS = 0x290, // HID type SEL
HID_CONSUMER_AC_INSERT_ROW = 0x291, // HID type SEL
HID_CONSUMER_AC_INSERT_COLUMN = 0x292, // HID type SEL
HID_CONSUMER_AC_INSERT_FILE = 0x293, // HID type SEL
HID_CONSUMER_AC_INSERT_PICTURE = 0x294, // HID type SEL
HID_CONSUMER_AC_INSERT_OBJECT = 0x295, // HID type SEL
HID_CONSUMER_AC_INSERT_SYMBOL = 0x296, // HID type SEL
HID_CONSUMER_AC_SAVE_AND_CLOSE = 0x297, // HID type SEL
HID_CONSUMER_AC_RENAME = 0x298, // HID type SEL
HID_CONSUMER_AC_MERGE = 0x299, // HID type SEL
HID_CONSUMER_AC_SPLIT = 0x29A, // HID type SEL
HID_CONSUMER_AC_DISRIBUTE_HORIZONTALLY = 0x29B, // HID type SEL
HID_CONSUMER_AC_DISTRIBUTE_VERTICALLY = 0x29C, // HID type SEL
};

View File

@ -1,6 +1,8 @@
#pragma once
#include <stdint.h>
#include "../_compiler.h"
// USB U16 helpers
#define U16(_high, _low) ((uint16_t)(((_high) << 8) | (_low)))
#define U16_HIGH(_u16) ((uint8_t)(((_u16) >> 8) & 0x00ff))

View File

@ -26,7 +26,7 @@ static uint8_t su8VendorCount = 0;
void Tas_USBD_Open(void) {
g_usbd_CtrlMaxPktSize = gpDeviceDescriptor->bMaxPacketSize0;
USBD->ATTR = 0x650; // Disable D+ and USB controller
CLK_SysTickLongDelay(3000 ms);
CLK_SysTickLongDelay(1000 ms);
USBD->ATTR = 0x7D0;
USBD_SET_SE0();
}
@ -189,18 +189,10 @@ static inline void Tas_USBD_GetDescriptor(void) {
break;
case USB_STRING_VENDOR: {
const char *szVendor;
if (su8VendorCount < 2) {
szVendor = gszVendorInitial;
su8VendorCount++;
} else {
szVendor = gszVendor;
}
uint8_t u8Len = strlen(szVendor);
uint8_t u8Len = strlen(gszVendor);
u8Str[0] = 2 + u8Len * 2;
for (uint8_t i = 0; i < u8Len; i++) {
u8Str[2 + i * 2] = szVendor[i];
u8Str[2 + i * 2] = gszVendor[i];
u8Str[2 + i * 2 + 1] = 0;
}
break;
@ -215,8 +207,10 @@ static inline void Tas_USBD_GetDescriptor(void) {
break;
}
case USB_STRING_SERIAL: {
// TODO: I think it might be a u16 then two u8s?
// Need to check the TRM
// The unique ID is technically 3 words, but I'm pretty sure only the last one
// really changes. The TRM has no details regarding this.
// There's no harm using all three as our serial, so to stay on the safe side
// that's what we do.
uint32_t u32serial;
FMC_Open();

View File

@ -1,16 +1,27 @@
#include "tasoller.h"
uint8_t volatile g_u8Suspend = 0;
uint8_t volatile g_u8UsbState = 0;
uint8_t volatile gu8VComDTEPresent = 0;
uint8_t g_u8Idle = 0;
uint8_t g_u8Protocol = 0;
uint8_t gHidSetReport[64];
STR_VCOM_LINE_CODING gLineCoding = { 0, 0, 0, 0 };
uint16_t gCtrlSignal;
uint32_t volatile g_u32OutToggle = 0;
#define STALL_CONTROL \
do { \
USBD_SetStall(EP_CTRL_IN); \
USBD_SetStall(EP_CTRL_OUT); \
} while (0)
#define CONTROL_IN_DONE \
do { \
USBD_SET_DATA1(EP_CTRL_IN); \
USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0); \
} while (0)
void USBD_IRQHandler(void) {
uint32_t u32IntSts = USBD_GET_INT_FLAG();
uint32_t u32State = USBD_GET_BUS_STATE();
@ -21,8 +32,10 @@ void USBD_IRQHandler(void) {
if (USBD_IS_ATTACHED()) {
USBD_ENABLE_USB();
g_u8UsbState &= ~USB_STATE_FLOATING;
} else {
USBD_DISABLE_USB();
g_u8UsbState |= USB_STATE_FLOATING;
}
}
@ -38,17 +51,15 @@ void USBD_IRQHandler(void) {
USBD_ENABLE_USB();
Tas_USBD_SwReset();
g_u32OutToggle = 0;
g_u8Suspend = 0;
g_u8UsbState &= ~USB_STATE_SUSPEND;
}
if (u32State & USBD_STATE_SUSPEND) {
// Enter power down to wait USB attached; enable USB but disable PHY
g_u8Suspend = 1;
USBD_DISABLE_PHY();
g_u8UsbState |= USB_STATE_SUSPEND;
}
if (u32State & USBD_STATE_RESUME) {
// Enable USB and enable PHY
USBD_ENABLE_USB();
g_u8Suspend = 0;
g_u8UsbState &= ~USB_STATE_SUSPEND;
}
}
@ -173,30 +184,28 @@ void Tas_USBD_ClassRequest(void) {
default:
// Setup error, stall the device
USBD_SetStall(EP_CTRL_IN);
USBD_SetStall(EP_CTRL_OUT);
STALL_CONTROL;
break;
}
} else {
// Host to device
switch (setup.bRequest) {
case SET_CONTROL_LINE_STATE:
// TODO: Use bit[0] (DTR) to identify connection state
// Is RTS worth using?
if (setup.wIndex == USBD_ITF_CDC_CMD) gCtrlSignal = setup.wValue;
// Status stage
USBD_SET_DATA1(EP_CTRL_IN);
USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0);
if (setup.wIndex == USBD_ITF_CDC_CMD) {
gu8VComDTEPresent = setup.wValue & 1;
CONTROL_IN_DONE;
} else {
STALL_CONTROL;
}
break;
case SET_LINE_CODING:
if (setup.setLineCoding.wInterface == USBD_ITF_CDC_CMD)
if (setup.setLineCoding.wInterface == USBD_ITF_CDC_CMD) {
Tas_USBD_PrepareCtrlOut(&gLineCoding, sizeof gLineCoding, NULL);
// Status stage
USBD_SET_DATA1(EP_CTRL_IN);
USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0);
CONTROL_IN_DONE;
} else {
STALL_CONTROL;
}
break;
case SET_REPORT:
@ -216,22 +225,17 @@ void Tas_USBD_ClassRequest(void) {
// // Status stage
// Tas_USBD_PrepareCtrlIn(NULL, 0);
}
USBD_SET_DATA1(EP_CTRL_IN);
USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0);
CONTROL_IN_DONE;
break;
case SET_IDLE:
g_u8Idle = setup.hidSetIdle.bDuration;
// Status stage
USBD_SET_DATA1(EP_CTRL_IN);
USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0);
CONTROL_IN_DONE;
break;
case SET_PROTOCOL:
g_u8Protocol = setup.hidSetProtocol.wProtocol;
// Status stage
USBD_SET_DATA1(EP_CTRL_IN);
USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0);
CONTROL_IN_DONE;
break;
default:

View File

@ -3,7 +3,8 @@
#define BUF_SIZE_RX 512
#define BUF_SIZE_TX 512
volatile uint8_t gu8VcomReady = 0;
volatile uint8_t gu8VcomDTEPresent = 0;
volatile uint8_t gu8VComReady = 0;
static volatile uint8_t gau8ComRbuf[BUF_SIZE_RX];
static volatile uint16_t gu16ComRbytes = 0;
@ -26,10 +27,10 @@ void _USB_VCOM_Tick_Tx(void) {
uint32_t u32Len;
if (gu32TxSize != 0) return;
// Check wether we have new COM Rx data to send to USB or not
// Check whether we have new COM Rx data to send to USB or not
if (!gu16ComRbytes) {
// Prepare a zero packet if previous packet size is USBD_CDC_IN_MAX_SIZE and
// no more data to send at this moment to note Host the transfer has been done
// Prepare a zero packet if previous packet size is USBD_CDC_IN_MAX_SIZE and no more data to
// send at this moment to notify to the Host that the transfer has been done
u32Len = USBD_GET_PAYLOAD_LEN(EP_CDC_IN);
if (u32Len == USBD_CDC_IN_MAX_SIZE) USBD_SET_PAYLOAD_LEN(EP_CDC_IN, 0);
return;

25
src/vcom.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include <stdint.h>
typedef struct __packed {
uint32_t u32DTERate; // Baud rate
uint8_t u8CharFormat; // Stop bit
uint8_t u8ParityType; // Parity
uint8_t u8DataBits; // Data bits
} STR_VCOM_LINE_CODING;
extern STR_VCOM_LINE_CODING gLineCoding;
extern volatile int8_t gi8BulkOutReady;
extern volatile uint8_t *gpu8RxBuf;
extern volatile uint32_t gu32RxSize;
extern volatile uint32_t gu32TxSize;
extern volatile uint8_t gu8VComDTEPresent;
extern volatile uint8_t gu8VComReady;
void USB_VCOM_Write(uint8_t u8Char);
uint16_t USB_VCOM_Available(void);
uint8_t USB_VCOM_Read(void);
void USB_VCOM_Tick(void);
void USB_VCOM_PurgeTx(void);