diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index ec4d575..b328053 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -9,4 +9,3 @@ set(CMAKE_C_STANDARD 11) pico_sdk_init() add_subdirectory(src) -add_subdirectory(lib/pico-mpr121 mpr121) diff --git a/firmware/README.md b/firmware/README.md deleted file mode 100644 index 8d29661..0000000 --- a/firmware/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# IIDX Pico Firmware - -Features: -* 1000Hz polling rate. -* HID lights. -* RGB turntable. -* Configuration save. -* Customizable through board_defs.h -* Dymanic settings. - diff --git a/firmware/lib/pico-mpr121/CMakeLists.txt b/firmware/lib/pico-mpr121/CMakeLists.txt deleted file mode 100644 index bf7542b..0000000 --- a/firmware/lib/pico-mpr121/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -add_library(pico-mpr121 INTERFACE) - -target_include_directories(pico-mpr121 - INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/include -) - -target_link_libraries(pico-mpr121 - INTERFACE - hardware_i2c -) - -target_sources(pico-mpr121 - INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/mpr121.c - ${CMAKE_CURRENT_LIST_DIR}/include/mpr121.h -) diff --git a/firmware/lib/pico-mpr121/include/mpr121.h b/firmware/lib/pico-mpr121/include/mpr121.h deleted file mode 100644 index f5abf5d..0000000 --- a/firmware/lib/pico-mpr121/include/mpr121.h +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (c) 2021-2022 Antonio González - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef _MPR121_H_ -#define _MPR121_H_ - -#include "pico.h" -#include "hardware/i2c.h" - -/** \file mpr121.h - * \brief Library for using an MPR121-based touch sensor with the - * Raspberry Pi Pico - * -*/ - -typedef struct mpr121_sensor { - i2c_inst_t *i2c_port; - uint8_t i2c_addr; - // uint8_t i2c_sda; - // uint8_t ic2_scl; -} mpr121_sensor_t; - -/*! \brief MPR121 register map - */ -enum mpr121_register { - MPR121_TOUCH_STATUS_REG = 0x00u, - MPR121_OUT_OF_RANGE_STATUS_0_REG = 0x02u, - MPR121_OUT_OF_RANGE_STATUS_1_REG = 0x03u, - MPR121_ELECTRODE_FILTERED_DATA_REG = 0x04u, - MPR121_BASELINE_VALUE_REG = 0x1Eu, - // Registers 0x2B ~ 0x7F are control and configuration registers - MPR121_MAX_HALF_DELTA_RISING_REG = 0x2Bu, - MPR121_NOISE_HALF_DELTA_RISING_REG = 0x2Cu, - MPR121_NOISE_COUNT_LIMIT_RISING_REG = 0x2Du, - MPR121_FILTER_DELAY_COUNT_RISING_REG = 0x2Eu, - MPR121_MAX_HALF_DELTA_FALLING_REG = 0x2Fu, - MPR121_NOISE_HALF_DELTA_FALLING_REG = 0x30u, - MPR121_NOISE_COUNT_LIMIT_FALLING_REG = 0x31u, - MPR121_FILTER_DELAY_COUNT_FALLING_REG = 0x32u, - MPR121_NOISE_HALF_DELTA_TOUCHED_REG = 0x33u, - MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG = 0x34u, - MPR121_FILTER_DELAY_COUNT_TOUCHED_REG = 0x35u, - // (ELEPROX 0x36 .. 0x40) - MPR121_TOUCH_THRESHOLD_REG = 0x41u, - MPR121_RELEASE_THRESHOLD_REG = 0x42u, - // (ELEPROX 0x59 .. 0x5A) - MPR121_DEBOUNCE_REG = 0x5Bu, - MPR121_AFE_CONFIG_REG = 0x5Cu, - MPR121_FILTER_CONFIG_REG = 0x5Du, - MPR121_ELECTRODE_CONFIG_REG = 0x5Eu, - MPR121_ELECTRODE_CURRENT_REG = 0x5Fu, - MPR121_ELECTRODE_CHARGE_TIME_REG = 0x6Cu, - MPR121_GPIO_CTRL_0_REG = 0x73u, - MPR121_GPIO_CTRL_1_REG = 0x74u, - MPR121_GPIO_DATA_REG = 0x75u, - MPR121_GPIO_DIRECTION_REG = 0x76u, - MPR121_GPIO_ENABLE_REG = 0x77u, - MPR121_GPIO_DATA_SET_REG = 0x78u, - MPR121_GPIO_DATA_CLEAR_REG = 0x79u, - MPR121_GPIO_DATA_TOGGLE_REG = 0x7Au, - MPR121_AUTOCONFIG_CONTROL_0_REG = 0x7Bu, - MPR121_AUTOCONFIG_CONTROL_1_REG = 0x7Cu, - MPR121_AUTOCONFIG_USL_REG = 0x7Du, - MPR121_AUTOCONFIG_LSL_REG = 0x7Eu, - MPR121_AUTOCONFIG_TARGET_REG = 0x7Fu, - MPR121_SOFT_RESET_REG = 0x80u -}; - -/*! \brief Initialise the MPR121 and configure registers - * - * The default parameters used here to configure the sensor are as in - * the MPR121 Quick Start Guide (AN3944). - * - * \param i2c_port The I2C instance, either i2c0 or i2c1 - * \param i2c_addr The I2C address of the MPR121 device - * \param sensor Pointer to the structure that stores the MPR121 info - */ -void mpr121_init(i2c_inst_t *i2c_port, uint8_t i2c_addr, - mpr121_sensor_t *sensor); - -/*! \brief Write a value to the specified register - * - * \param reg The register address - * \param val The value to write - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_write(enum mpr121_register reg, uint8_t val, - mpr121_sensor_t *sensor) { - uint8_t buf[] = {reg, val}; - i2c_write_blocking(sensor->i2c_port, sensor->i2c_addr, buf, 2, - false); -} - -/*! \brief Read a byte from the specified register - * - * \param reg The register address - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_read(enum mpr121_register reg, uint8_t *dst, - mpr121_sensor_t *sensor) { - i2c_write_blocking(sensor->i2c_port, sensor->i2c_addr, ®, 1, - true); - i2c_read_blocking(sensor->i2c_port, sensor->i2c_addr, dst, 1, - false); -} - -/*! \brief Read a 2-byte value from the specified register - * - * \param reg The register address - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_read16(enum mpr121_register reg, uint16_t *dst, - mpr121_sensor_t *sensor) { - uint8_t vals[2]; - i2c_write_blocking(sensor->i2c_port, sensor->i2c_addr, ®, 1, - true); - i2c_read_blocking(sensor->i2c_port, sensor->i2c_addr, vals, 2, - false); - *dst = vals[1] << 8 | vals[0]; -} - -/*! \brief Set touch and release thresholds - * - * From the MPR121 datasheet (section 5.6): - * > In a typical application, touch threshold is in the range 4--16, - * > and it is several counts larger than the release threshold. This - * > is to provide hysteresis and to prevent noise and jitter. - * - * \param touch Touch threshold in the range 0--255 - * \param release Release threshold in the range 0--255 - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_set_thresholds(uint8_t touch, uint8_t release, - mpr121_sensor_t *sensor) { - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - if (config != 0){ - // Stop mode - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - } - - for (uint8_t i=0; i<12; i++) { - mpr121_write(MPR121_TOUCH_THRESHOLD_REG + i * 2, touch, sensor); - mpr121_write(MPR121_RELEASE_THRESHOLD_REG + i * 2, release, - sensor); - } - - if (config != 0){ - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); - } -} - -/*! \brief Enable only the number of electrodes specified - * - * \param nelec Number of electrodes to enable - * \param sensor Pointer to the structure that stores the MPR121 info - * - * E.g. if `nelec` is 3, electrodes 0 to 2 will be enabled; if `nelec` - * is 6, electrodes 0 to 5 will be enabled. From the datasheet: - * "Enabling specific channels will save the scan time and sensing - * field power spent on the unused channels." - */ -static void mpr121_enable_electrodes(uint8_t nelec, - mpr121_sensor_t *sensor){ - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - - // Clear bits 3-0, which controls the operation of the 12 - // electrodes. - config &= ~0x0f; - - // Set number of electrodes enabled - config |= nelec; - - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); -} - -/*! \brief Read the touch/release status of all 13 input channels - * - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - * - * In the value read, bits 11-0 represent electrodes 11 to 0, - * respectively, and bit 12 is the proximity detection channel. Each - * bit represent the status of these channels: 1 if the channel is - * touched, 0 if it is released. - */ -static void mpr121_touched(uint16_t *dst, mpr121_sensor_t *sensor) { - mpr121_read16(MPR121_TOUCH_STATUS_REG, dst, sensor); - *dst &= 0x0fff; -} - -/*! \brief Determine whether an electrode has been touched - * - * \param electrode Electrode number - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_is_touched(uint8_t electrode, bool *dst, - mpr121_sensor_t *sensor){ - uint16_t touched; - mpr121_touched(&touched, sensor); - *dst = (bool) ((touched >> electrode) & 1); -} - -/*! \brief Read an electrode's filtered data value - * - * \param electrode Electrode number - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - * - * The data range of the filtered data is 0 to 1024. - * \sa mpr121_baseline_value - */ -static void mpr121_filtered_data(uint8_t electrode, uint16_t *dst, - mpr121_sensor_t *sensor){ - mpr121_read16(MPR121_ELECTRODE_FILTERED_DATA_REG + (electrode * 2), - dst, sensor); - // Filtered data is 10-bit - *dst &= 0x3ff; -} - -/*! \brief Read an electrode's baseline value - * - * \param electrode Electrode number - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - * - * From the MPR112 datasheet: - * > Along with the 10-bit electrode filtered data output, each channel - * > also has a 10-bit baseline value. These values are the output of - * > the internal baseline filter operation tracking the slow-voltage - * > variation of the background capacitance change. Touch/release - * > detection is made based on the comparison between the 10-bit - * > electrode filtered data and the 10-bit baseline value. - * - * > Although internally the baseline value is 10-bit, users can only - * > access the 8 MSB of the 10-bit baseline value through the baseline - * > value registers. - * - * \sa mpr121_filtered_data - */ -static void mpr121_baseline_value(uint8_t electrode, uint16_t *dst, - mpr121_sensor_t *sensor){ - uint8_t baseline; - mpr121_read(MPR121_BASELINE_VALUE_REG + electrode, &baseline, - sensor); - // From the datasheet: Although internally the baseline value is - // 10-bit, users can only access the 8 MSB of the 10-bit baseline - // value through the baseline value registers. The read out from the - // baseline register must be left shift two bits before comparing it - // with the 10-bit electrode data. - *dst = baseline << 2; -} - -/*! \brief Set the Max Half Delta - * - * The Max Half Delta determines the largest magnitude of variation to - * pass through the third level filter. See application note MPR121 - * Baseline System (AN3891) for details. - * - * \param rising Value in the range 1~63 - * \param falling Value in the range 1~63 - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_set_max_half_delta(uint8_t rising, uint8_t falling, - mpr121_sensor_t *sensor) { - // Read current configuration then enter stop mode - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - // Write MHD values - mpr121_write(MPR121_MAX_HALF_DELTA_RISING_REG, rising, sensor); - mpr121_write(MPR121_MAX_HALF_DELTA_FALLING_REG, falling, sensor); - // Re-enable electrodes - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); -} - -/*! \brief Set the Noise Half Delta - * - * The Noise Half Delta determines the incremental change when - * non-noise drift is detected. See application note MPR121 Baseline - * System (AN3891) for details. - * - * \param rising Value in the range 1~63 - * \param falling Value in the range 1~63 - * \param touched Value in the range 1~63 - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_set_noise_half_delta(uint8_t rising, uint8_t falling, - uint8_t touched, mpr121_sensor_t *sensor) { - // Read current configuration then enter stop mode - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - // Write NHD values - mpr121_write(MPR121_NOISE_HALF_DELTA_RISING_REG, rising, sensor); - mpr121_write(MPR121_NOISE_HALF_DELTA_FALLING_REG, falling, sensor); - mpr121_write(MPR121_NOISE_HALF_DELTA_TOUCHED_REG, touched, sensor); - // Re-enable electrodes - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); -} - -/*! \brief Set the Noise Count Limit - * - * The Noise Count Limit determines the number of samples consecutively - * greater than the Max Half Delta necessary before it can be - * determined that it is non-noise. See application note MPR121 Baseline - * System (AN3891) for details. - * - * \param rising Value in the range 0~255 - * \param falling Value in the range 0~255 - * \param touched Value in the range 0~255 - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_set_noise_count_limit(uint8_t rising, - uint8_t falling, uint8_t touched, mpr121_sensor_t *sensor) { - // Read current configuration then enter stop mode - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - // Write new NCL values - mpr121_write(MPR121_NOISE_COUNT_LIMIT_RISING_REG, rising, sensor); - mpr121_write(MPR121_NOISE_COUNT_LIMIT_FALLING_REG, falling, sensor); - mpr121_write(MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG, touched, sensor); - // Re-enable electrodes - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); -} - -/*! \brief Set the Filter Delay Limit - * - * The Filter Delay Limit determines the rate of operation of the - * filter. A larger number makes it operate slower. See application - * note MPR121 Baseline System (AN3891) for details. - * - * \param rising Value in the range 0~255 - * \param falling Value in the range 0~255 - * \param touched Value in the range 0~255 - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_set_filter_delay_limit(uint8_t rising, - uint8_t falling, uint8_t touched, mpr121_sensor_t *sensor) { - // Read current configuration then enter stop mode - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - // Write new FDL values - mpr121_write(MPR121_FILTER_DELAY_COUNT_RISING_REG, rising, sensor); - mpr121_write(MPR121_FILTER_DELAY_COUNT_FALLING_REG, falling, - sensor); - mpr121_write(MPR121_FILTER_DELAY_COUNT_TOUCHED_REG, touched, - sensor); - // Re-enable electrodes - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); -} - -#endif diff --git a/firmware/lib/pico-mpr121/mpr121.c b/firmware/lib/pico-mpr121/mpr121.c deleted file mode 100644 index 7586675..0000000 --- a/firmware/lib/pico-mpr121/mpr121.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2021-2022 Antonio González - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "mpr121.h" - -void mpr121_init(i2c_inst_t *i2c_port, uint8_t i2c_addr, - mpr121_sensor_t *sensor) { - sensor->i2c_port = i2c_port; - sensor->i2c_addr = i2c_addr; - - // Enter stop mode by setting ELEPROX_EN and ELE_EN bits to zero. - // This is needed because register write operations can only take - // place in stop mode. - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - - // Writing 0x80 (SOFT_RESET) with 0x63 asserts soft reset. - mpr121_write(MPR121_SOFT_RESET_REG, 0x63, sensor); - - // == Capacitance sensing settings (AN2889), Filtering and ========= - // timing settings (AN3890) - - // These settings are configured in two registers: the Filter and - // Global CDC CDT Configuration registers (0x5C, 0x5D). - // - // Charge-discharge current (CDC) and charge-discharge time (CDT) - // can be configured globally or on a per-electrode basis. Here, - // The global CDC and CDT values are set to their defaults, and then - // these values are overriden by independently configuring each - // electrode (auto-configuration). - - // Filter/global CDC configuration register (0x5C) - // - // First filter iterations (FFI), bits 7-6. Number of samples taken - // as input to the first level of filtering. Default is 0b00 (sets - // samples taken to 6) - // - // Charge-discharge current (CDC), bits 5-0. Sets the value of - // charge-discharge current applied to the electrode. Max is 63 µA - // in 1 µA steps. Default is 0b010000 (16 µA) - // - // AFE configuration register default, 0b00010000 = 0x10 - mpr121_write(MPR121_AFE_CONFIG_REG, 0x10, sensor); - - // Filter/global CDC configuration register (0x5D) - // - // Charge discharge time (CDT), bits 7-5. Selects the global value - // of charge time applied to electrode. The maximum is 32 μs, - // programmable as 2^(n-2) μs. Default is 0b001 (time is set to - // 0.5 µs) - // - // Second filter iterations (SFI), bits 4-3. Selects the number of - // samples taken for the second level filter. Default is 0b00 - // (number of samples is set to 4) - // - // Electrode sample interval (ESI), bits 2-0. Controls the sampling - // rate of the device. The maximum is 128 ms, programmable to 2^n - // ms. Decrease this value for better response time, increase to - // save power. Default is 0b100 (period set to 16 ms). - // - // Filter configuration register default, 0b00100100 = 0x24 - // I do not need power saving features but I want fast responses, - // so I set this to 0x20. - mpr121_write(MPR121_FILTER_CONFIG_REG, 0x20, sensor); - - // Auto-configuration - // - // Sets automatically charge current (CDC) and time (CDT) values for - // each electrode. - // - // Autoconfig USL register: the upper limit for the - // auto-configuration. This value (and those that follow below) - // were calculated based on Vdd = 3.3 V and following the equations - // in NXP Application Note AN3889. - // USL = 201 = 0xC9 - mpr121_write(MPR121_AUTOCONFIG_USL_REG, 0xC9, sensor); - - // Autoconfig target level register: the target level for the - // auto-configuration baseline search. - // TL = 181 = 0xB5 - mpr121_write(MPR121_AUTOCONFIG_TARGET_REG, 0xB5, sensor); - - // Autoconfig LSL register: the lower limit for the - // auto-configuration. - // LSL = 131 = 0x83 - mpr121_write(MPR121_AUTOCONFIG_LSL_REG, 0x83, sensor); - - // Autoconfiguration control register. Default value is 0b00001011 = - // 0x0B, where: - // - // First filter iterations (FFI), bits 7-6. Must be the same value - // of FFI as in register MPR121_AFE_CONFIG_REG (0x5C) above; - // default is 0b00. - // - // Retry, bits 5-4. Default is disabled, 0b00. - // - // Baseline value adjust (BVA), bits 3-2. This value must be the - // same as the CL (calibration lock) value in the Electrode - // Configuration Register, below, i.e. 0b10. - // - // Automatic Reconfiguration Enable (ARE), bit 1. Default is 0b1, - // enabled. - // - // Automatic Reconfiguration Enable (ACE), bit 0. Default is 0b1, - // enabled. - mpr121_write(MPR121_AUTOCONFIG_CONTROL_0_REG, 0x0B, sensor); - - // == Baseline system (AN3891) ===================================== - - // Maximum Half Delta (MHD): Determines the largest magnitude of - // variation to pass through the baseline filter. The range of the - // effective value is 1~63. - mpr121_write(MPR121_MAX_HALF_DELTA_RISING_REG, 0x01, sensor); - mpr121_write(MPR121_MAX_HALF_DELTA_FALLING_REG, 0x01, sensor); - - // Noise Half Delta (NHD): Determines the incremental change when - // non-noise drift is detected. The range of the effective value is - // 1~63. - mpr121_write(MPR121_NOISE_HALF_DELTA_RISING_REG, 0x01, sensor); - mpr121_write(MPR121_NOISE_HALF_DELTA_FALLING_REG, 0x01, sensor); - mpr121_write(MPR121_NOISE_HALF_DELTA_TOUCHED_REG, 0x01, sensor); - - // Noise Count Limit (NCL): Determines the number of samples - // consecutively greater than the Max Half Delta value. This is - // necessary to determine that it is not noise. The range of the - // effective value is 0~255. - mpr121_write(MPR121_NOISE_COUNT_LIMIT_RISING_REG, 0x00, sensor); - mpr121_write(MPR121_NOISE_COUNT_LIMIT_FALLING_REG, 0xFF, sensor); - mpr121_write(MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG, 0x00, sensor); - - // Filter Delay Count Limit (FDL): Determines the operation rate of - // the filter. A larger count limit means the filter delay is - // operating more slowly. The range of the effective value is 0~255. - mpr121_write(MPR121_FILTER_DELAY_COUNT_RISING_REG, 0x00, sensor); - mpr121_write(MPR121_FILTER_DELAY_COUNT_FALLING_REG, 0x02, sensor); - mpr121_write(MPR121_FILTER_DELAY_COUNT_TOUCHED_REG, 0x00, sensor); - - // == Debounce and thresholds (AN3892) ============================= - - // Debounce. Value range for each is 0~7. - // Bits 2-0, debounce touch (DT). - // Bits 6-4, debounce release (DR). - mpr121_write(MPR121_DEBOUNCE_REG, 0x00, sensor); - - // Touch and release threshold values for all electrodes. - for (uint8_t i=0; i<12; i++) { - mpr121_write(MPR121_TOUCH_THRESHOLD_REG + i * 2, 0x0F, sensor); - mpr121_write(MPR121_RELEASE_THRESHOLD_REG + i * 2, 0x0A, sensor); - } - - // Electrode Configuration Register (ECR, 0x5E). This must be the - // last register to write to because setting ELEPROX_EN and/or - // ELE_EN to non-zero puts the sensor in Run Mode. - // - // Calibration lock (CL), bits 7-6. The default on reset is 0b00 - // (CL enabled). Here I set this instead to 0b10 because this - // enables baseline tracking with initial baseline value loaded - // with the 5 high bits of the first electrode data value, which - // makes the sensor stabilise sooner. Note that ths value must - // match BVA bits in the Auto-configure Control Register above. - // - // Proximity enable (ELEPROX_EN), bits 5-4. Default, 0b00 - // (proximity detection disabled). - // - // Electrode enabled (ELE_EN), bits 3-0. Default, 0b1100 (enable - // all 12 electrodes). - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x8C, sensor); -} \ No newline at end of file diff --git a/firmware/mai_pico.uf2 b/firmware/mai_pico.uf2 deleted file mode 100644 index e48902b..0000000 Binary files a/firmware/mai_pico.uf2 and /dev/null differ diff --git a/firmware/src/CMakeLists.txt b/firmware/src/CMakeLists.txt index 1c2a198..f34b749 100644 --- a/firmware/src/CMakeLists.txt +++ b/firmware/src/CMakeLists.txt @@ -4,8 +4,8 @@ set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip) function(make_firmware board board_def) pico_sdk_init() add_executable(${board} - main.c buttons.c rgb.c save.c config.c setup.c - usb_descriptors.c) + main.c touch.c rgb.c save.c config.c cmd.c + mpr121.c usb_descriptors.c) target_compile_definitions(${board} PUBLIC ${board_def}) pico_enable_stdio_usb(${board} 1) pico_enable_stdio_uart(${board} 0) @@ -21,13 +21,13 @@ function(make_firmware board board_def) target_link_libraries(${board} PRIVATE pico_multicore pico_stdlib hardware_pio hardware_pwm hardware_flash hardware_adc hardware_i2c hardware_watchdog - tinyusb_device tinyusb_board pico-mpr121) + tinyusb_device tinyusb_board) pico_add_extra_outputs(${board}) add_custom_command(TARGET ${board} POST_BUILD - COMMAND cp ${board}.uf2 /mnt/d/Code/iidx_pico/firmware/build) + COMMAND cp ${board}.uf2 ${CMAKE_CURRENT_LIST_DIR}/..) endfunction() -make_firmware(mai_pico BOARD_IIDX_PICO) +make_firmware(mai_pico BOARD_MAI_PICO) diff --git a/firmware/src/board_defs.h b/firmware/src/board_defs.h index 899dc34..e991554 100644 --- a/firmware/src/board_defs.h +++ b/firmware/src/board_defs.h @@ -1,32 +1,19 @@ /* - * IIDX Controller Board Definitions + * Mai Controller Board Definitions * WHowe */ -#if defined BOARD_IIDX_PICO -/* List of button pins */ -#define BUTTON_DEF { 8, 7, 6, 5, 4, 3, 2, 12, 11, 10, 9, 1, 0 } +#if defined BOARD_MAI_PICO -#define BUTTON_RGB_PIN 13 -#define BUTTON_RGB_ORDER GRB // or RGB +#define I2C_PORT i2c0 +#define I2C_SDA 16 +#define I2C_SCL 17 +#define I2C_FREQ 733*1000 -#define BUTTON_RGB_NUM 11 -#define BUTTON_RGB_MAP { 6, 0, 5, 1, 4, 2, 3, 7, 8, 9, 10} - - -#define TT_RGB_PIN 28 -#define TT_RGB_ORDER GRB // or RGB - -#define TT_AS5600_ANALOG 26 -#define TT_AS5600_SCL 27 -#define TT_AS5600_SDA 26 -#define TT_AS5600_I2C i2c1 - -// Alternative I2C pins -//#define TT_AS5600_SCL 21 -//#define TT_AS5600_SDA 20 -//#define TT_AS5600_I2C i2c0 +#define RGB_PIN 2 +#define RGB_ORDER GRB // or RGB +#define NKRO_KEYMAP "1aqz2swx3dec4frv5gtb6hyn7jum8ki90olp,." #else #endif diff --git a/firmware/src/buttons.c b/firmware/src/buttons.c deleted file mode 100644 index 10d0033..0000000 --- a/firmware/src/buttons.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Controller Buttons - * WHowe - * - * A button consists of a switch and an LED - */ - -#include "buttons.h" - -#include -#include - -#include "bsp/board.h" -#include "hardware/gpio.h" - -#include "board_defs.h" - -static const uint8_t BUTTON_GPIOS[] = BUTTON_DEF; -#define BUTTON_NUM (sizeof(BUTTON_GPIOS)) - -static bool sw_val[BUTTON_NUM]; /* true if pressed */ -static uint64_t sw_freeze_time[BUTTON_NUM]; - -#define LIMIT_MAX(a, max, def) { if (a > max) a = def; } - -void button_init() -{ - for (int i = 0; i < BUTTON_NUM; i++) { - sw_val[i] = false; - sw_freeze_time[i] = 0; - int8_t gpio = BUTTON_GPIOS[i]; - gpio_init(gpio); - gpio_set_function(gpio, GPIO_FUNC_SIO); - gpio_set_dir(gpio, GPIO_IN); - gpio_pull_up(gpio); - } -} - -uint8_t button_num() -{ - return BUTTON_NUM; -} - -uint8_t button_gpio(uint8_t id) -{ - return BUTTON_GPIOS[id]; -} - -/* If a switch flips, it freezes for a while */ -#define DEBOUNCE_FREEZE_TIME_US 5000 -uint16_t button_read() -{ - uint64_t now = time_us_64(); - uint16_t buttons = 0; - - for (int i = BUTTON_NUM - 1; i >= 0; i--) { - bool sw_pressed = !gpio_get(BUTTON_GPIOS[i]); - - if (now >= sw_freeze_time[i]) { - if (sw_pressed != sw_val[i]) { - sw_val[i] = sw_pressed; - sw_freeze_time[i] = now + DEBOUNCE_FREEZE_TIME_US; - } - } - - buttons <<= 1; - if (sw_val[i]) { - buttons |= 1; - } - } - - return buttons; -} diff --git a/firmware/src/buttons.h b/firmware/src/buttons.h deleted file mode 100644 index e5c5019..0000000 --- a/firmware/src/buttons.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Controller Buttons - * WHowe - */ - -#ifndef BUTTONS_H -#define BUTTONS_H - -#include - -void button_init(); -uint8_t button_num(); -uint8_t button_gpio(uint8_t id); - -uint16_t button_read(); - -#endif diff --git a/firmware/src/cmd.c b/firmware/src/cmd.c new file mode 100644 index 0000000..b912ded --- /dev/null +++ b/firmware/src/cmd.c @@ -0,0 +1,416 @@ +#include +#include +#include +#include +#include + +#include "pico/stdio.h" +#include "pico/stdlib.h" + +#include "config.h" +#include "touch.h" +#include "save.h" + +#define SENSE_LIMIT_MAX 9 +#define SENSE_LIMIT_MIN -9 + +#define MAX_COMMANDS 20 +#define MAX_PARAMETERS 5 +#define MAX_PARAMETER_LENGTH 20 + +const char *mai_prompt = "mai_pico>"; + +typedef void (*cmd_handler_t)(int argc, char *argv[]); + +static const char *commands[MAX_COMMANDS]; +static cmd_handler_t handlers[MAX_COMMANDS]; +static int num_commands = 0; + +static void register_command(const char *cmd, cmd_handler_t handler) +{ + if (num_commands < MAX_COMMANDS) { + commands[num_commands] = cmd; + handlers[num_commands] = handler; + num_commands++; + } +} + +// return -1 if not matched, return -2 if ambiguous +static int match_prefix(const char *str[], int num, const char *prefix) +{ + int match = -1; + bool found = false; + + for (int i = 0; (i < num) && str[i]; i++) { + if (strncmp(str[i], prefix, strlen(prefix)) == 0) { + if (found) { + return -2; + } + found = true; + match = i; + } + } + + return match; +} + +static void handle_help(int argc, char *argv[]) +{ + printf("Available commands:\n"); + for (int i = 0; i < num_commands; i++) { + printf("%s\n", commands[i]); + } +} + +static void disp_colors() +{ + printf("[Colors]\n"); + printf(" Key on: %06x, off: %06x\n", + mai_cfg->colors.key_on, mai_cfg->colors.key_off); +} + +static void disp_style() +{ + printf("[Style]\n"); + printf(" Key: %d, Level: %d\n", mai_cfg->style.key, mai_cfg->style.level); +} + +static void disp_sense() +{ + printf("[Sense]\n"); + printf(" Filter: %d, %d\n", mai_cfg->sense.filter >> 4, mai_cfg->sense.filter & 0xf); + printf(" Sensitivity (global: %+d):\n", mai_cfg->sense.global); + printf(" | 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|15|16|\n"); + printf(" ---------------------------------------------------\n"); + printf(" A |"); + for (int i = 0; i < 16; i++) { + printf("%+2d|", mai_cfg->sense.keys[i * 2]); + } + printf("\n B |"); + for (int i = 0; i < 16; i++) { + printf("%+2d|", mai_cfg->sense.keys[i * 2 + 1]); + } + printf("\n"); + printf(" Debounce (touch, release): %d, %d\n", + mai_cfg->sense.debounce_touch, mai_cfg->sense.debounce_release); +} + +static void disp_hid() +{ + printf("[HID]\n"); + printf(" Joy: %s, NKRO: %s.\n", + mai_cfg->hid.joy ? "on" : "off", + mai_cfg->hid.nkro ? "on" : "off" ); +} + +void handle_display(int argc, char *argv[]) +{ + const char *usage = "Usage: display [colors|style|tof|sense|hid]\n"; + if (argc > 1) { + printf(usage); + return; + } + + if (argc == 0) { + disp_colors(); + disp_style(); + disp_sense(); + disp_hid(); + return; + } + + const char *choices[] = {"colors", "style", "sense", "hid"}; + switch (match_prefix(choices, 5, argv[0])) { + case 0: + disp_colors(); + break; + case 1: + disp_style(); + break; + case 2: + disp_sense(); + break; + case 3: + disp_hid(); + break; + default: + printf(usage); + break; + } +} + +static int fps[2]; +void fps_count(int core) +{ + static uint32_t last[2] = {0}; + static int counter[2] = {0}; + + counter[core]++; + + uint32_t now = time_us_32(); + if (now - last[core] < 1000000) { + return; + } + last[core] = now; + fps[core] = counter[core]; + counter[core] = 0; +} + +static void handle_fps(int argc, char *argv[]) +{ + printf("FPS: core 0: %d, core 1: %d\n", fps[0], fps[1]); +} + +static void handle_hid(int argc, char *argv[]) +{ + const char *usage = "Usage: hid \n"; + if (argc != 1) { + printf(usage); + return; + } + + const char *choices[] = {"joy", "nkro", "both"}; + int match = match_prefix(choices, 3, argv[0]); + if (match < 0) { + printf(usage); + return; + } + + mai_cfg->hid.joy = ((match == 0) || (match == 2)) ? 1 : 0; + mai_cfg->hid.nkro = ((match == 1) || (match == 2)) ? 1 : 0; + config_changed(); + disp_hid(); +} + +static int extract_non_neg_int(const char *param, int len) +{ + if (len == 0) { + len = strlen(param); + } + int result = 0; + for (int i = 0; i < len; i++) { + if (!isdigit(param[i])) { + return -1; + } + result = result * 10 + param[i] - '0'; + } + return result; +} + +static void handle_filter(int argc, char *argv[]) +{ + const char *usage = "Usage: filter \n" + " first, second: 0..3\n"; + if ((argc < 2) || (argc > 2)) { + printf(usage); + return; + } + + int ffi = extract_non_neg_int(argv[0], 0); + int sfi = extract_non_neg_int(argv[1], 0); + + if ((ffi < 0) || (ffi > 3) || (sfi < 0) || (sfi > 3)) { + printf(usage); + return; + } + + mai_cfg->sense.filter = (ffi << 4) | sfi; + + touch_update_config(); + config_changed(); + disp_sense(); +} + +static uint8_t *extract_key(const char *param) +{ + int len = strlen(param); + + int offset; + if (toupper(param[len - 1]) == 'A') { + offset = 0; + } else if (toupper(param[len - 1]) == 'B') { + offset = 1; + } else { + return NULL; + } + + int id = extract_non_neg_int(param, len - 1) - 1; + if ((id < 0) || (id > 15)) { + return NULL; + } + + return &mai_cfg->sense.keys[id * 2 + offset]; +} + +static void sense_do_op(int8_t *target, char op) +{ + if (op == '+') { + if (*target < SENSE_LIMIT_MAX) { + (*target)++; + } + } else if (op == '-') { + if (*target > SENSE_LIMIT_MIN) { + (*target)--; + } + } else if (op == '0') { + *target = 0; + } +} + +static void handle_sense(int argc, char *argv[]) +{ + const char *usage = "Usage: sense [key|*] <+|-|0>\n" + "Example:\n" + " >sense +\n" + " >sense -\n" + " >sense 1A +\n" + " >sense 13B -\n"; + " >sense * 0\n"; + if ((argc < 1) || (argc > 2)) { + printf(usage); + return; + } + + const char *op = argv[argc - 1]; + if ((strlen(op) != 1) || !strchr("+-0", op[0])) { + printf(usage); + return; + } + + if (argc == 1) { + sense_do_op(&mai_cfg->sense.global, op[0]); + } else { + if (strcmp(argv[0], "*") == 0) { + for (int i = 0; i < 32; i++) { + sense_do_op(&mai_cfg->sense.keys[i], op[0]); + } + } else { + uint8_t *key = extract_key(argv[0]); + if (!key) { + printf(usage); + return; + } + sense_do_op(key, op[0]); + } + } + + touch_update_config(); + config_changed(); + disp_sense(); +} + +static void handle_debounce(int argc, char *argv[]) +{ + const char *usage = "Usage: debounce [release]\n" + " touch, release: 0..7\n"; + if ((argc < 1) || (argc > 2)) { + printf(usage); + return; + } + + int touch = mai_cfg->sense.debounce_touch; + int release = mai_cfg->sense.debounce_release; + if (argc >= 1) { + touch = extract_non_neg_int(argv[0], 0); + } + if (argc == 2) { + release = extract_non_neg_int(argv[1], 0); + } + + if ((touch < 0) || (release < 0) || + (touch > 7) || (release > 7)) { + printf(usage); + return; + } + + mai_cfg->sense.debounce_touch = touch; + mai_cfg->sense.debounce_release = release; + + touch_update_config(); + config_changed(); + disp_sense(); +} + +static void handle_save() +{ + save_request(true); +} + +static void handle_factory_reset() +{ + config_factory_reset(); + printf("Factory reset done.\n"); +} + +void cmd_init() +{ + register_command("?", handle_help); + register_command("display", handle_display); + register_command("fps", handle_fps); + register_command("hid", handle_hid); + register_command("filter", handle_filter); + register_command("sense", handle_sense); + register_command("debounce", handle_debounce); + register_command("save", handle_save); + register_command("factory", config_factory_reset); +} + +static char cmd_buf[256]; +static int cmd_len = 0; + +static void process_cmd() +{ + char *argv[MAX_PARAMETERS]; + int argc; + + char *cmd = strtok(cmd_buf, " \n"); + + if (strlen(cmd) == 0) { + return; + } + + argc = 0; + while ((argc < MAX_PARAMETERS) && + (argv[argc] = strtok(NULL, " \n")) != NULL) { + argc++; + } + + int match = match_prefix(commands, num_commands, cmd); + if (match == -2) { + printf("Ambiguous command.\n"); + return; + } + if (match == -1) { + printf("Unknown command.\n"); + handle_help(0, NULL); + return; + } + + handlers[match](argc, argv); +} + +void cmd_run() +{ + int c = getchar_timeout_us(0); + if (c == EOF) { + return; + } + + if ((c != '\n') && (c != '\r')) { + if (cmd_len < sizeof(cmd_buf) - 2) { + cmd_buf[cmd_len] = c; + printf("%c", c); + cmd_len++; + } + return; + } + + cmd_buf[cmd_len] = '\0'; + cmd_len = 0; + + printf("\n"); + + process_cmd(); + + printf(mai_prompt); +} diff --git a/firmware/src/cmd.h b/firmware/src/cmd.h new file mode 100644 index 0000000..d50c77d --- /dev/null +++ b/firmware/src/cmd.h @@ -0,0 +1,13 @@ +/* + * Mai Controller Command Line + * WHowe + */ + +#ifndef CMD_H +#define CMD_H + +void cmd_init(); +void cmd_run(); +void fps_count(int core); + +#endif diff --git a/firmware/src/config.c b/firmware/src/config.c index 1e6e849..1929063 100644 --- a/firmware/src/config.c +++ b/firmware/src/config.c @@ -1,68 +1,63 @@ /* - * Controller Config Data + * Controller Config and Runtime Data * WHowe * * Config is a global data structure that stores all the configuration + * Runtime is something to share between files. */ #include "config.h" #include "save.h" -iidx_cfg_t *iidx_cfg; +mai_cfg_t *mai_cfg; -static iidx_cfg_t default_cfg = { - .key_off = { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}, - .key_on = { {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, - {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, {40,40,40}, +static mai_cfg_t default_cfg = { + .colors = { + .key_on = 0x00FF00, + .key_off = 0x000000, }, - .tt_led = { - .start = 0, - .num = 24, - .effect = 0, - .param = 0, - .mode = 0, + .style = { + .key = 0, + .level = 7, }, - .tt_sensor = { - .mode = 2, - .deadzone = 1, - .ppr = 1, + .sense = { + .filter = 0x11, + .debounce_touch = 1, + .debounce_release = 2, + }, + .hid = { + .joy = 1, + .nkro = 0, }, - .effects = { - .e1 = 255, - .e2 = 128, - .e3 = 128, - .e4 = 128, - }, - .level = 128, - .konami = false, }; +mai_runtime_t *mai_runtime; + static void config_loaded() { - if (iidx_cfg->tt_led.num == 0) { - iidx_cfg->tt_led.num = 24; + if (mai_cfg->style.level > 10) { + mai_cfg->style.level = default_cfg.style.level; config_changed(); } - if ((iidx_cfg->tt_led.start > 8) || - (iidx_cfg->tt_led.start + iidx_cfg->tt_led.num > 128)) { - iidx_cfg->tt_led.start = 0; - iidx_cfg->tt_led.num = 24; + if ((mai_cfg->sense.filter & 0x0f) > 3 || + ((mai_cfg->sense.filter >> 4) & 0x0f) > 3) { + mai_cfg->sense.filter = default_cfg.sense.filter; config_changed(); } - if (iidx_cfg->tt_sensor.deadzone > 2) { - iidx_cfg->tt_sensor.deadzone = 0; + if ((mai_cfg->sense.global > 9) || (mai_cfg->sense.global < -9)) { + mai_cfg->sense.global = default_cfg.sense.global; config_changed(); } - if (iidx_cfg->tt_led.mode > 2) { - iidx_cfg->tt_led.mode = 0; - config_changed(); + for (int i = 0; i < 32; i++) { + if ((mai_cfg->sense.keys[i] > 9) || (mai_cfg->sense.keys[i] < -9)) { + mai_cfg->sense.keys[i] = default_cfg.sense.keys[i]; + config_changed(); + } } - if (iidx_cfg->tt_sensor.mode > 3) { - iidx_cfg->tt_sensor.mode = 2; - config_changed(); - } - if (iidx_cfg->tt_sensor.ppr > 3) { - iidx_cfg->tt_sensor.ppr = 1; + if ((mai_cfg->sense.debounce_touch > 7) | + (mai_cfg->sense.debounce_release > 7)) { + mai_cfg->sense.debounce_touch = default_cfg.sense.debounce_touch; + mai_cfg->sense.debounce_release = default_cfg.sense.debounce_release; config_changed(); } } @@ -74,11 +69,11 @@ void config_changed() void config_factory_reset() { - *iidx_cfg = default_cfg; + *mai_cfg = default_cfg; save_request(true); } void config_init() { - iidx_cfg = (iidx_cfg_t *)save_alloc(sizeof(iidx_cfg), &default_cfg, config_loaded); + mai_cfg = (mai_cfg_t *)save_alloc(sizeof(*mai_cfg), &default_cfg, config_loaded); } diff --git a/firmware/src/config.h b/firmware/src/config.h index 9d7353c..94e1cd4 100644 --- a/firmware/src/config.h +++ b/firmware/src/config.h @@ -9,38 +9,34 @@ #include #include -typedef struct __attribute ((packed)) { - uint8_t h; // hue; - uint8_t s; // saturation; - uint8_t v; // value; -} hsv_t; +typedef struct __attribute__((packed)) { + struct { + uint32_t key_on; + uint32_t key_off; + } colors; + struct { + uint8_t key; + uint8_t level; + } style; + struct { + int8_t filter; + int8_t global; + uint8_t debounce_touch; + uint8_t debounce_release; + int8_t keys[34]; + } sense; + struct { + uint8_t joy : 4; + uint8_t nkro : 4; + } hid; +} mai_cfg_t; -typedef struct __attribute ((packed)) { - hsv_t key_off[11]; - hsv_t key_on[11]; - struct { - uint8_t start; - uint8_t num; - uint8_t effect; - uint8_t param; - uint8_t mode; /* 0: on, 1: reversed, 2: off */ - } tt_led; - struct { - uint8_t mode; /* 0: analog, 1: analog reversed, 2: i2c, 3: i2c reversed */ - uint8_t deadzone; /* only for analog */ - uint8_t ppr; /* 0: 256, 1: 128, 2: 96, 3: 64, other: 256 */ - } tt_sensor; - struct { - uint8_t e1; - uint8_t e2; - uint8_t e3; - uint8_t e4; - } effects; - uint8_t level; /* led brightness limit */ - bool konami; /* konami spoof */ -} iidx_cfg_t; +typedef struct { + uint16_t fps[2]; +} mai_runtime_t; -extern iidx_cfg_t *iidx_cfg; +extern mai_cfg_t *mai_cfg; +extern mai_runtime_t *mai_runtime; void config_init(); void config_changed(); // Notify the config has changed diff --git a/firmware/src/mai_pico.code-workspace b/firmware/src/mai_pico.code-workspace deleted file mode 100644 index 0de5248..0000000 --- a/firmware/src/mai_pico.code-workspace +++ /dev/null @@ -1,38 +0,0 @@ -{ - "folders": [ - { - "path": "../.." - }, - { - "path": "../../../pico-examples" - } - ], - "settings": { - "files.associations": { - "turntable.h": "c", - "stdint.h": "c", - "_default_types.h": "c", - "limits.h": "c", - "cdefs.h": "c", - "gpio.h": "c", - "pico.h": "c", - "flash.h": "c", - "bootrom.h": "c", - "multicore.h": "c", - "stdbool.h": "c", - "sync.h": "c", - "stdio.h": "c", - "config.h": "c", - "stdlib.h": "c", - "setup.h": "c", - "config_autogen.h": "c", - "board_defs.h": "c", - "rgb.h": "c", - "type_traits": "c", - "cmath": "c", - "*.tcc": "c", - "cinttypes": "c", - "cstdlib": "c" - } - } -} \ No newline at end of file diff --git a/firmware/src/main.c b/firmware/src/main.c index 318a95a..12e02b5 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -3,202 +3,183 @@ * WHowe */ +#include #include #include +#include "pico/stdio.h" +#include "pico/stdlib.h" #include "bsp/board.h" #include "pico/multicore.h" #include "pico/bootrom.h" -#include "pico/stdio.h" -#include "hardware/watchdog.h" -#include "hardware/gpio.h" -#include "hardware/adc.h" -#include "hardware/i2c.h" -#include "mpr121.h" +#include "hardware/gpio.h" +#include "hardware/sync.h" +#include "hardware/structs/ioqspi.h" +#include "hardware/structs/sio.h" + #include "tusb.h" #include "usb_descriptors.h" -/* Measure the time of a function call */ -#define RUN_TIME(func) \ - { uint64_t _t = time_us_64(); func; \ - printf(#func ":%lld\n", time_us_64() - _t); } +#include "board_defs.h" -struct { - uint16_t buttons; - uint8_t joy[6]; -} hid_report; +#include "save.h" +#include "config.h" +#include "cmd.h" + +#include "touch.h" +#include "rgb.h" + +struct __attribute__((packed)) { + uint16_t buttons; // 16 buttons; see JoystickButtons_t for bit mapping + uint8_t HAT; // HAT switch; one nibble w/ unused nibble + uint32_t axis; // slider touch data + uint8_t VendorSpec; +} hid_joy; + +struct __attribute__((packed)) { + uint8_t modifier; + uint8_t keymap[15]; +} hid_nkro, sent_hid_nkro; void report_usb_hid() { if (tud_hid_ready()) { - hid_report.joy[2] = 0; - hid_report.joy[3] = 64; - hid_report.joy[4] = 128; - hid_report.joy[5] = 192; - tud_hid_n_report(0x00, REPORT_ID_JOYSTICK, &hid_report, sizeof(hid_report)); + hid_joy.HAT = 0; + hid_joy.VendorSpec = 0; + if (mai_cfg->hid.joy) { + tud_hid_n_report(0x00, REPORT_ID_JOYSTICK, &hid_joy, sizeof(hid_joy)); + } + if (mai_cfg->hid.nkro && + (memcmp(&hid_nkro, &sent_hid_nkro, sizeof(hid_nkro)) != 0)) { + sent_hid_nkro = hid_nkro; + tud_hid_n_report(0x02, 0, &sent_hid_nkro, sizeof(sent_hid_nkro)); + } } } -static bool request_core1_pause = false; - -static void pause_core1(bool pause) +static void gen_joy_report() { - request_core1_pause = pause; - if (pause) { - sleep_ms(5); /* wait for any IO ops to finish */ + hid_joy.axis = 0; + for (int i = 0; i < 16; i++) { + if (touch_touched(i * 2)) { + hid_joy.axis |= 1 << (30 - i * 2); + } + if (touch_touched(i * 2 + 1)) { + hid_joy.axis |= 1 << (31 - i * 2); + } + + } + hid_joy.axis ^= 0x80808080; // some magic number from CrazyRedMachine + hid_joy.buttons = 0x0; +} + +const uint8_t keycode_table[128][2] = { HID_ASCII_TO_KEYCODE }; +const char keymap[38 + 1] = NKRO_KEYMAP; // 32 keys, 6 air keys, 1 terminator +static void gen_nkro_report() +{ + for (int i = 0; i < 32; i++) { + uint8_t code = keycode_table[keymap[i]][1]; + uint8_t byte = code / 8; + uint8_t bit = code % 8; + if (touch_touched(i)) { + hid_nkro.keymap[byte] |= (1 << bit); + } else { + hid_nkro.keymap[byte] &= ~(1 << bit); + } + } + for (int i = 0; i < 6; i++) { + uint8_t code = keycode_table[keymap[32 + i]][1]; + uint8_t byte = code / 8; + uint8_t bit = code % 8; + if (hid_joy.buttons & (1 << i)) { + hid_nkro.keymap[byte] |= (1 << bit); + } else { + hid_nkro.keymap[byte] &= ~(1 << bit); + } } } +static uint64_t last_hid_time = 0; + +static void run_lights() +{ + uint64_t now = time_us_64(); + if (now - last_hid_time < 1000000) { + return; + } + + const uint32_t colors[] = {0x000000, 0x0000ff, 0xff0000, 0xffff00, + 0x00ff00, 0x00ffff, 0xffffff}; + + for (int i = 0; i < 15; i++) { + int x = 15 - i; + uint8_t r = (x & 0x01) ? 10 : 0; + uint8_t g = (x & 0x02) ? 10 : 0; + uint8_t b = (x & 0x04) ? 10 : 0; + rgb_gap_color(i, rgb32(r, g, b, false)); + } + + for (int i = 0; i < 16; i++) { + bool r = touch_touched(i * 2); + bool g = touch_touched(i * 2 + 1); + rgb_set_color(30 - i * 2, rgb32(r ? 80 : 0, g ? 80 : 0, 0, false)); + } +} + +static mutex_t core1_io_lock; static void core1_loop() { -} - - -// I2C definitions: port and pin numbers -#define MPR121_PORT i2c0 -#define MPR121_SDA 16 -#define MPR121_SCL 17 - -// MPR121 I2C definitions: address and frequency. -#define MPR121_ADDR 0x5A -#define MPR121_I2C_FREQ 400000 - -// Touch and release thresholds. -#define MPR121_TOUCH_THRESHOLD 16 -#define MPR121_RELEASE_THRESHOLD 10 - -#define GP2Y_PORT i2c0 -#define GP2Y_I2C_FREQ 200000 -#define GP2Y_SDA 16 -#define GP2Y_SCL 17 - -static void core0_loop_gp2y() -{ - i2c_init(GP2Y_PORT, GP2Y_I2C_FREQ); - gpio_set_function(GP2Y_SDA, GPIO_FUNC_I2C); - gpio_set_function(GP2Y_SCL, GPIO_FUNC_I2C); - gpio_pull_up(GP2Y_SDA); - gpio_pull_up(GP2Y_SCL); - - while(1) { - tud_task(); - - hid_report.buttons = 0xcccc; - report_usb_hid(); - - uint8_t reg = 0x00; - i2c_write_blocking(GP2Y_PORT, 0x40, ®, 1, true); - - uint16_t v = 0x00; - i2c_read_blocking(GP2Y_PORT, 0x5e, (uint8_t *)&v, 2, false); - - printf("%4x\n", v); - - sleep_ms(1); - } -} - - -static void core0_loop_adc() -{ - adc_init(); - adc_gpio_init(28); - adc_select_input(2); - - while(1) { - tud_task(); - - hid_report.buttons = 0xcccc; - report_usb_hid(); - - uint16_t result = adc_read(); - printf("%6o\n", result); - sleep_ms(1); - } -} - -static void core0_loop_mpr121() -{ - // Initialise I2C. - i2c_init(MPR121_PORT, MPR121_I2C_FREQ); - gpio_set_function(MPR121_SDA, GPIO_FUNC_I2C); - gpio_set_function(MPR121_SCL, GPIO_FUNC_I2C); - gpio_pull_up(MPR121_SDA); - gpio_pull_up(MPR121_SCL); - - struct mpr121_sensor mpr121[3]; - for (int m = 0; m < 3; m++) - { - mpr121_init(MPR121_PORT, MPR121_ADDR + m, mpr121 + m); - mpr121_set_thresholds(MPR121_TOUCH_THRESHOLD, - MPR121_RELEASE_THRESHOLD, mpr121 + m); - // Enable only one touch sensor (electrode 0). - mpr121_enable_electrodes(12, mpr121 + m); - } - - int16_t baseline[34] = {0}; - - uint32_t counter[34] = {0}; - for (int c = 0; c < 1000; c++) { - tud_task(); - hid_report.buttons = 0xcccc; - report_usb_hid(); - for (int i = 0; i < 34; i++) { - int16_t touch_data; - mpr121_baseline_value(i % 12, &touch_data, mpr121 + i / 12); - counter[i] += touch_data; + while (1) { + if (mutex_try_enter(&core1_io_lock, NULL)) { + run_lights(); + rgb_update(); + mutex_exit(&core1_io_lock); } - } - - for (int i = 0; i < 34; i++) { - baseline[i] = counter[i] / 1000; - } - - while(1) { - tud_task(); - - hid_report.buttons = 0xcccc; - report_usb_hid(); - - for (int i = 0; i < 34; i++) { - int16_t touch_data; - mpr121_baseline_value(i % 12, &touch_data, mpr121 + i / 12); - int16_t display_data = (baseline[i] - touch_data) / 4; - if (display_data) { - printf("%2d", display_data); - } else { - printf(" "); - } - printf("%c", i % 12 == 11 ? ':' : ' '); - } - printf("\n"); + fps_count(1); + sleep_ms(1); } } static void core0_loop() { - core0_loop_mpr121(); + while(1) { + cmd_run(); + save_loop(); + fps_count(0); + + touch_update(); + + gen_joy_report(); + gen_nkro_report(); + report_usb_hid(); + tud_task(); + } } void init() { + sleep_ms(100); + set_sys_clock_khz(150000, true); board_init(); tusb_init(); - stdio_init_all(); + config_init(); + mutex_init(&core1_io_lock); + save_init(0xca34cafe, &core1_io_lock); + + touch_init(); + rgb_init(); + + cmd_init(); } int main(void) { - sleep_ms(1000); - init(); - //multicore_launch_core1(core1_loop); - + multicore_launch_core1(core1_loop); core0_loop(); - return 0; } @@ -209,6 +190,7 @@ uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen) { + printf("Get from USB %d-%d\n", report_id, report_type); return 0; } @@ -218,10 +200,22 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) { - if ((report_id == REPORT_ID_LIGHTS) && - (report_type == HID_REPORT_TYPE_OUTPUT)) { - if (bufsize >= 0) { - return; + if (report_type == HID_REPORT_TYPE_OUTPUT) { + if (report_id == REPORT_ID_LED_touch_16) { + rgb_set_brg(0, buffer, bufsize / 3); + } else if (report_id == REPORT_ID_LED_touch_15) { + rgb_set_brg(16, buffer, bufsize / 3); + } else if (report_id == REPORT_ID_LED_TOWER_6) { + rgb_set_brg(31, buffer, bufsize / 3); } + last_hid_time = time_us_64(); + return; + } + + if (report_type == HID_REPORT_TYPE_FEATURE) { + if (report_id == REPORT_ID_LED_COMPRESSED) { + } + last_hid_time = time_us_64(); + return; } } diff --git a/firmware/src/mpr121.c b/firmware/src/mpr121.c new file mode 100644 index 0000000..c4790af --- /dev/null +++ b/firmware/src/mpr121.c @@ -0,0 +1,200 @@ +/* + * MP121 Captive Touch Sensor + * WHowe + * + */ + +#include +#include "hardware/i2c.h" + +#include "mpr121.h" +#include "board_defs.h" + +#define IO_TIMEOUT_US 1000 + +#define TOUCH_THRESHOLD_BASE 17 +#define RELEASE_THRESHOLD_BASE 12 + +#define MPR121_TOUCH_STATUS_REG 0x00 +#define MPR121_OUT_OF_RANGE_STATUS_0_REG 0x02 +#define MPR121_OUT_OF_RANGE_STATUS_1_REG 0x03 +#define MPR121_ELECTRODE_FILTERED_DATA_REG 0x04 +#define MPR121_BASELINE_VALUE_REG 0x1E + +#define MPR121_MAX_HALF_DELTA_RISING_REG 0x2B +#define MPR121_NOISE_HALF_DELTA_RISING_REG 0x2C +#define MPR121_NOISE_COUNT_LIMIT_RISING_REG 0x2D +#define MPR121_FILTER_DELAY_COUNT_RISING_REG 0x2E +#define MPR121_MAX_HALF_DELTA_FALLING_REG 0x2F +#define MPR121_NOISE_HALF_DELTA_FALLING_REG 0x30 +#define MPR121_NOISE_COUNT_LIMIT_FALLING_REG 0x31 +#define MPR121_FILTER_DELAY_COUNT_FALLING_REG 0x32 +#define MPR121_NOISE_HALF_DELTA_TOUCHED_REG 0x33 +#define MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG 0x34 +#define MPR121_FILTER_DELAY_COUNT_TOUCHED_REG 0x35 + +#define MPR121_TOUCH_THRESHOLD_REG 0x41 +#define MPR121_RELEASE_THRESHOLD_REG 0x42 + +#define MPR121_DEBOUNCE_REG 0x5B +#define MPR121_AFE_CONFIG_REG 0x5C +#define MPR121_FILTER_CONFIG_REG 0x5D +#define MPR121_ELECTRODE_CONFIG_REG 0x5E +#define MPR121_ELECTRODE_CURRENT_REG 0x5F +#define MPR121_ELECTRODE_CHARGE_TIME_REG 0x6C +#define MPR121_GPIO_CTRL_0_REG 0x73 +#define MPR121_GPIO_CTRL_1_REG 0x74 +#define MPR121_GPIO_DATA_REG 0x75 +#define MPR121_GPIO_DIRECTION_REG 0x76 +#define MPR121_GPIO_ENABLE_REG 0x77 +#define MPR121_GPIO_DATA_SET_REG 0x78 +#define MPR121_GPIO_DATA_CLEAR_REG 0x79 +#define MPR121_GPIO_DATA_TOGGLE_REG 0x7A +#define MPR121_AUTOCONFIG_CONTROL_0_REG 0x7B +#define MPR121_AUTOCONFIG_CONTROL_1_REG 0x7C +#define MPR121_AUTOCONFIG_USL_REG 0x7D +#define MPR121_AUTOCONFIG_LSL_REG 0x7E +#define MPR121_AUTOCONFIG_TARGET_REG 0x7F +#define MPR121_SOFT_RESET_REG 0x80 + +static void write_reg(uint8_t addr, uint8_t reg, uint8_t val) +{ + uint8_t buf[] = {reg, val}; + i2c_write_blocking_until(I2C_PORT, addr, buf, 2, false, + time_us_64() + IO_TIMEOUT_US); +} + +static uint8_t read_reg(uint8_t addr, uint8_t reg) +{ + uint8_t value; + i2c_write_blocking_until(I2C_PORT, addr, ®, 1, true, + time_us_64() + IO_TIMEOUT_US); + i2c_read_blocking_until(I2C_PORT, addr, &value, 1, false, + time_us_64() + IO_TIMEOUT_US); + return value; +} + +void mpr121_init(uint8_t i2c_addr) +{ + write_reg(i2c_addr, 0x80, 0x63); // Soft reset MPR121 if not reset correctly + + //touch pad baseline filter + //rising: baseline quick rising + write_reg(i2c_addr, 0x2B, 0x01); // Max half delta Rising + write_reg(i2c_addr, 0x2C, 0x01); // Noise half delta Rising + write_reg(i2c_addr, 0x2D, 0x00); // Noise count limit Rising + write_reg(i2c_addr, 0x2E, 0x00); // Delay limit Rising + + //falling: baseline slow falling + write_reg(i2c_addr, 0x2F, 0x01); // Max half delta Falling + write_reg(i2c_addr, 0x30, 0x01); // Noise half delta Falling + write_reg(i2c_addr, 0x31, 0xFF); // Noise count limit Falling + write_reg(i2c_addr, 0x32, 0x0F); // Delay limit Falling + + //touched: baseline keep + write_reg(i2c_addr, 0x33, 0x00); // Noise half delta Touched + write_reg(i2c_addr, 0x34, 0x00); // Noise count Touched + write_reg(i2c_addr, 0x35, 0x00); // Delay limit Touched + + //Touch pad threshold + for (int i = 0; i < 12; i++) { + write_reg(i2c_addr, 0x41 + i * 2, TOUCH_THRESHOLD_BASE); + write_reg(i2c_addr, 0x42 + i * 2, RELEASE_THRESHOLD_BASE); + } + + //touch and release debounce + write_reg(i2c_addr, 0x5B, 0x00); + + //AFE and filter configuration + write_reg(i2c_addr, 0x5C, 0b00010000); // AFES=6 samples, same as AFES in 0x7B, Global CDC=16uA + write_reg(i2c_addr, 0x5D, 0b00101000); // CT=0.5us, TDS=4samples, TDI=16ms + write_reg(i2c_addr, 0x5E, 0x80); // Set baseline calibration enabled, baseline loading 5MSB + + //Auto Configuration + write_reg(i2c_addr, 0x7B, 0b00001011); // AFES=6 samples, same as AFES in 0x5C + // retry=2b00, no retry, + // BVA=2b10, load 5MSB after AC, + // ARE/ACE=2b11, auto configuration enabled + //write_reg(i2c_addr, 0x7C,0x80); // Skip charge time search, use setting in 0x5D, + // OOR, AR, AC IE disabled + // Not used. Possible Proximity CDC shall over 63uA + // if only use 0.5uS CDT, the TGL for proximity cannot meet + // Possible if manually set Register0x72=0x03 + // (Auto configure result) alone. + write_reg(i2c_addr, 0x7D, 0xc8); // AC up limit /C8/BD/C0/9C + write_reg(i2c_addr, 0x7E, 0x82); // AC low limit /82/7A/7C/65 + write_reg(i2c_addr, 0x7F, 0xb4); // AC target /B4/AA/AC/8C target for /3.0V/2.8V/1.8V + write_reg(i2c_addr, 0x5E, 0x8C); // Run 12 touch, CL=2b10, load 5MSB to baseline +} + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +static void mpr121_read_many(uint8_t addr, uint8_t reg, uint8_t *buf, size_t n) +{ + i2c_write_blocking_until(I2C_PORT, addr, ®, 1, true, + time_us_64() + IO_TIMEOUT_US); + i2c_read_blocking_until(I2C_PORT, addr, buf, n, false, + time_us_64() + IO_TIMEOUT_US * n / 2); +} + +static void mpr121_read_many16(uint8_t addr, uint8_t reg, uint16_t *buf, size_t n) +{ + uint8_t vals[n * 2]; + mpr121_read_many(addr, reg, vals, n * 2); + for (int i = 0; i < n; i++) { + buf[i] = (vals[i * 2 + 1] << 8) | vals[i * 2]; + } +} + +uint16_t mpr121_touched(uint8_t addr) +{ + uint16_t touched; + mpr121_read_many16(addr, MPR121_TOUCH_STATUS_REG, &touched, 2); + return touched; +} + +static uint8_t mpr121_stop(uint8_t addr) +{ + uint8_t ecr = read_reg(addr, MPR121_ELECTRODE_CONFIG_REG); + write_reg(addr, MPR121_ELECTRODE_CONFIG_REG, ecr & 0xC0); + return ecr; +} + +static uint8_t mpr121_resume(uint8_t addr, uint8_t ecr) +{ + write_reg(addr, MPR121_ELECTRODE_CONFIG_REG, ecr); +} + +void mpr121_filter(uint8_t addr, uint8_t ffi, uint8_t sfi) +{ + uint8_t ecr = mpr121_stop(addr); + + uint8_t afe = read_reg(addr, MPR121_AFE_CONFIG_REG); + write_reg(addr, MPR121_AFE_CONFIG_REG, (afe & 0x3f) | ffi << 6); + uint8_t acc = read_reg(addr, MPR121_AUTOCONFIG_CONTROL_0_REG); + write_reg(addr, MPR121_AUTOCONFIG_CONTROL_0_REG, (acc & 0x3f) | ffi << 6); + uint8_t fcr = read_reg(addr, MPR121_FILTER_CONFIG_REG); + write_reg(addr, MPR121_FILTER_CONFIG_REG, (fcr & 0xe7) | (sfi & 3) << 3); + + mpr121_resume(addr, ecr); +} + +void mpr121_sense(uint8_t addr, int8_t sense, int8_t *sense_keys) +{ + uint8_t ecr = mpr121_stop(addr); + for (int i = 0; i < 12; i++) { + int8_t delta = sense + sense_keys[i]; + write_reg(addr, MPR121_TOUCH_THRESHOLD_REG + i * 2, + TOUCH_THRESHOLD_BASE - delta); + write_reg(addr, MPR121_RELEASE_THRESHOLD_REG + i * 2, + RELEASE_THRESHOLD_BASE - delta / 2); + } + mpr121_resume(addr, ecr); +} + +void mpr121_debounce(uint8_t addr, uint8_t touch, uint8_t release) +{ + uint8_t ecr = mpr121_stop(addr); + write_reg(addr, 0x5B, (release & 0x07) << 4 | (touch & 0x07)); + mpr121_resume(addr, ecr); +} diff --git a/firmware/src/mpr121.h b/firmware/src/mpr121.h new file mode 100644 index 0000000..bf84a4c --- /dev/null +++ b/firmware/src/mpr121.h @@ -0,0 +1,18 @@ +/* + * MP121 Captive Touch Sensor + * WHowe + * + */ + +#ifndef MP121_H +#define MP121_H + +void mpr121_init(uint8_t addr); + +uint16_t mpr121_touched(uint8_t addr); + +void mpr121_filter(uint8_t addr, uint8_t ffi, uint8_t sfi); +void mpr121_sense(uint8_t addr, int8_t sense, int8_t *sense_keys); +void mpr121_debounce(uint8_t addr, uint8_t touch, uint8_t release); + +#endif diff --git a/firmware/src/rgb.c b/firmware/src/rgb.c index 82a9349..f1c3201 100644 --- a/firmware/src/rgb.c +++ b/firmware/src/rgb.c @@ -6,7 +6,6 @@ #include "rgb.h" -#include "buttons.h" #include #include #include @@ -23,12 +22,7 @@ #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) -static const uint8_t button_rgb_map[BUTTON_RGB_NUM] = BUTTON_RGB_MAP; - -static void trap() {} -static tt_effect_t effects[10] = { {trap, trap, trap, 0} }; -static size_t effect_num = 0; -static unsigned current_effect = 0; +static uint32_t rgb_buf[47]; // 16(Keys) + 15(Gaps) + 16(maximum ToF indicators) #define _MAP_LED(x) _MAKE_MAPPER(x) #define _MAKE_MAPPER(x) MAP_LED_##x @@ -40,10 +34,6 @@ static unsigned current_effect = 0; static inline uint32_t _rgb32(uint32_t c1, uint32_t c2, uint32_t c3, bool gamma_fix) { - c1 = c1 * iidx_cfg->level / 255; - c2 = c2 * iidx_cfg->level / 255; - c3 = c3 * iidx_cfg->level / 255; - if (gamma_fix) { c1 = ((c1 + 1) * (c1 + 1) - 1) >> 8; c2 = ((c2 + 1) * (c2 + 1) - 1) >> 8; @@ -53,7 +43,7 @@ static inline uint32_t _rgb32(uint32_t c1, uint32_t c2, uint32_t c3, bool gamma_ return (c1 << 16) | (c2 << 8) | (c3 << 0); } -uint32_t button_rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix) +uint32_t rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix) { #if BUTTON_RGB_ORDER == GRB return _rgb32(g, r, b, gamma_fix); @@ -62,250 +52,84 @@ uint32_t button_rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix) #endif } -uint32_t tt_rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix) +static void drive_led() { -#if TT_RGB_ORDER == GRB - return _rgb32(g, r, b, gamma_fix); -#else - return _rgb32(r, g, b, gamma_fix); -#endif -} - -uint8_t rgb_button_num() -{ - return BUTTON_RGB_NUM; -} - -uint8_t button_lights[BUTTON_RGB_NUM]; -uint32_t tt_led_buf[128] = {0}; -uint32_t tt_led_angle = 0; - -static uint32_t button_led_buf[BUTTON_RGB_NUM] = {0}; - -void set_effect(uint32_t index) -{ - if (index < effect_num) { - current_effect = index; - effects[current_effect].init(effects[current_effect].context); - } else { - current_effect = effect_num; - } -} - -void drive_led() -{ - for (int i = 0; i < ARRAY_SIZE(button_led_buf); i++) { - pio_sm_put_blocking(pio0, 0, button_led_buf[i] << 8u); - } - - if (iidx_cfg->tt_led.mode == 2) { + static uint64_t last = 0; + uint64_t now = time_us_64(); + if (now - last < 4000) { // no faster than 250Hz return; } + last = now; - for (int i = 0; i < iidx_cfg->tt_led.start; i++) { - pio_sm_put_blocking(pio1, 0, 0); + for (int i = 30; i >= 0; i--) { + pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u); } - for (int i = 0; i < TT_LED_NUM; i++) { - bool reversed = iidx_cfg->tt_led.mode & 0x01; - uint8_t id = reversed ? TT_LED_NUM - i - 1 : i; - pio_sm_put_blocking(pio1, 0, tt_led_buf[id] << 8u); - } - for (int i = 0; i < 8; i++) { // a few more to wipe out the last led - pio_sm_put_blocking(pio1, 0, 0); + for (int i = 31; i < ARRAY_SIZE(rgb_buf); i++) { + pio_sm_put_blocking(pio0, 0, rgb_buf[i] << 8u); } } -static uint32_t rgb32_from_hsv(hsv_t hsv) +void rgb_set_colors(const uint32_t *colors, unsigned index, size_t num) { - uint32_t region, remainder, p, q, t; - - if (hsv.s == 0) { - return hsv.v << 16 | hsv.v << 8 | hsv.v; - } - - region = hsv.h / 43; - remainder = (hsv.h % 43) * 6; - - p = (hsv.v * (255 - hsv.s)) >> 8; - q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8; - t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8; - - switch (region) { - case 0: - return hsv.v << 16 | t << 8 | p; - case 1: - return q << 16 | hsv.v << 8 | p; - case 2: - return p << 16 | hsv.v << 8 | t; - case 3: - return p << 16 | q << 8 | hsv.v; - case 4: - return t << 16 | p << 8 | hsv.v; - default: - return hsv.v << 16 | p << 8 | q; - } -} - -uint32_t button_hsv(hsv_t hsv) -{ - uint32_t rgb = rgb32_from_hsv(hsv); - uint32_t r = (rgb >> 16) & 0xff; - uint32_t g = (rgb >> 8) & 0xff; - uint32_t b = (rgb >> 0) & 0xff; -#if BUTTON_RGB_ORDER == GRB - return _rgb32(g, r, b, false); -#else - return _rgb32(r, g, b, false); -#endif -} - -uint32_t tt_hsv(hsv_t hsv) -{ - uint32_t rgb = rgb32_from_hsv(hsv); - uint32_t r = (rgb >> 16) & 0xff; - uint32_t g = (rgb >> 8) & 0xff; - uint32_t b = (rgb >> 0) & 0xff; -#if TT_RGB_ORDER == GRB - return _rgb32(g, r, b, false); -#else - return _rgb32(r, g, b, false); -#endif -} - -#define HID_EXPIRE_DURATION 1000000ULL -static uint64_t hid_expire_time = 0; - -static void button_lights_update() -{ - for (int i = 0; i < BUTTON_RGB_NUM; i++) { - int led = button_rgb_map[i]; - if (button_lights[i] > 0) { - button_led_buf[led] = button_hsv(iidx_cfg->key_on[i]); - } else { - button_led_buf[led] = button_hsv(iidx_cfg->key_off[i]); - } - } -} - -void rgb_set_angle(uint32_t angle) -{ - tt_led_angle = angle; - effects[current_effect].set_angle(angle); -} - -void rgb_set_button_light(uint16_t buttons) -{ - if (time_us_64() < hid_expire_time) { + if (index >= ARRAY_SIZE(rgb_buf)) { return; } - for (int i = 0; i < BUTTON_RGB_NUM; i++) { - uint16_t flag = 1 << i; - button_lights[i] = (buttons & flag) > 0 ? 0xff : 0; + if (index + num > ARRAY_SIZE(rgb_buf)) { + num = ARRAY_SIZE(rgb_buf) - index; + } + memcpy(&rgb_buf[index], colors, num * sizeof(*colors)); +} + +void rgb_set_color(unsigned index, uint32_t color) +{ + if (index >= ARRAY_SIZE(rgb_buf)) { + return; + } + rgb_buf[index] = color; +} + +void rgb_key_color(unsigned index, uint32_t color) +{ + if (index > 16) { + return; + } + rgb_buf[index * 2] = color; +} + +void rgb_gap_color(unsigned index, uint32_t color) +{ + if (index > 15) { + return; + } + rgb_buf[index * 2 + 1] = color; +} + +void rgb_set_brg(unsigned index, const uint8_t *brg_array, size_t num) +{ + if (index >= ARRAY_SIZE(rgb_buf)) { + return; + } + if (index + num > ARRAY_SIZE(rgb_buf)) { + num = ARRAY_SIZE(rgb_buf) - index; + } + for (int i = 0; i < num; i++) { + uint8_t b = brg_array[i * 3 + 0]; + uint8_t r = brg_array[i * 3 + 1]; + uint8_t g = brg_array[i * 3 + 2]; + rgb_buf[index + i] = rgb32(r, g, b, false); } } -void rgb_set_hid_light(uint8_t const *lights, uint8_t num) -{ - memcpy(button_lights, lights, num); - hid_expire_time = time_us_64() + HID_EXPIRE_DURATION; -} - -static void effect_update() -{ - effects[current_effect].update(effects[current_effect].context); -} - -#define FORCE_EXPIRE_DURATION 100000ULL -static uint64_t force_expire_time = 0; -uint32_t *force_buttons = NULL; -uint32_t *force_tt = NULL; - -void force_update() -{ - for (int i = 0; i < BUTTON_RGB_NUM; i++) { - int led = button_rgb_map[i]; - button_led_buf[led] = force_buttons[i]; - } - - memcpy(tt_led_buf, force_tt, TT_LED_NUM * sizeof(uint32_t)); -} - -void rgb_force_display(uint32_t *buttons, uint32_t *tt) -{ - force_buttons = buttons; - force_tt = tt; - force_expire_time = time_us_64() + FORCE_EXPIRE_DURATION; -} - -static void wipe_out_tt_led() -{ - sleep_ms(5); - for (int i = 0; i < 128; i++) { - pio_sm_put_blocking(pio1, 0, 0); - } - sleep_ms(5); -} - -static uint pio1_offset; -static bool pio1_running = false; - -static void pio1_run() -{ - gpio_set_drive_strength(TT_RGB_PIN, GPIO_DRIVE_STRENGTH_8MA); - ws2812_program_init(pio1, 0, pio1_offset, TT_RGB_PIN, 800000, false); -} - -static void pio1_stop() -{ - wipe_out_tt_led(); - pio_sm_set_enabled(pio1, 0, false); - - gpio_set_function(TT_RGB_PIN, GPIO_FUNC_SIO); - gpio_set_dir(TT_RGB_PIN, GPIO_IN); - gpio_disable_pulls(TT_RGB_PIN); -} void rgb_init() { uint pio0_offset = pio_add_program(pio0, &ws2812_program); - pio1_offset = pio_add_program(pio1, &ws2812_program); - gpio_set_drive_strength(BUTTON_RGB_PIN, GPIO_DRIVE_STRENGTH_2MA); - ws2812_program_init(pio0, 0, pio0_offset, BUTTON_RGB_PIN, 800000, false); - - /* We don't start the tt LED program yet */ -} - -static void follow_mode_change() -{ - bool pio1_should_run = (iidx_cfg->tt_led.mode != 2); - if (pio1_should_run == pio1_running) { - return; - } - pio1_running = pio1_should_run; - if (pio1_should_run) { - pio1_run(); - } else { - pio1_stop(); - } + gpio_set_drive_strength(RGB_PIN, GPIO_DRIVE_STRENGTH_2MA); + ws2812_program_init(pio0, 0, pio0_offset, RGB_PIN, 800000, false); } void rgb_update() { - follow_mode_change(); - set_effect(iidx_cfg->tt_led.effect); - if (time_us_64() > force_expire_time) { - effect_update(); - button_lights_update(); - } else { - force_update(); - } drive_led(); } - -void rgb_reg_tt_effect(tt_effect_t effect) -{ - effects[effect_num] = effect; - effect_num++; - effects[effect_num] = (tt_effect_t) { trap, trap, trap, 0 }; -} diff --git a/firmware/src/rgb.h b/firmware/src/rgb.h index a08ed74..903cca0 100644 --- a/firmware/src/rgb.h +++ b/firmware/src/rgb.h @@ -6,44 +6,23 @@ #ifndef RGB_H #define RGB_H +#include #include #include #include "config.h" void rgb_init(); -void rgb_set_hardware(uint16_t tt_start, uint16_t tt_num, bool tt_reversed); - -uint8_t rgb_button_num(); void rgb_update(); -void rgb_set_angle(uint32_t angle); -void rgb_set_level(uint8_t level); +uint32_t rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix); -void rgb_set_button_light(uint16_t buttons); -void rgb_set_hid_light(uint8_t const *lights, uint8_t num); +void rgb_set_colors(const uint32_t *colors, unsigned index, size_t num); +void rgb_set_color(unsigned index, uint32_t color); +void rgb_key_color(unsigned index, uint32_t color); +void rgb_gap_color(unsigned index, uint32_t color); -void rgb_force_display(uint32_t *keyboard, uint32_t *tt); - -typedef struct { - void (*init)(uint32_t context); - void (*set_angle)(uint32_t angle); - void (*update)(uint32_t context); - uint32_t context; -} tt_effect_t; - -void rgb_reg_tt_effect(tt_effect_t effect); - -extern uint32_t tt_led_buf[]; -#define TT_LED_NUM (iidx_cfg->tt_led.num) - -/* These global variables meant to be accessed by effect codes */ -extern uint32_t tt_led_angle; - - -uint32_t button_rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix); -uint32_t tt_rgb32(uint32_t r, uint32_t g, uint32_t b, bool gamma_fix); -uint32_t button_hsv(hsv_t hsv); -uint32_t tt_hsv(hsv_t hsv); +/* num of the rgb leds, num*3 bytes in the array */ +void rgb_set_brg(unsigned index, const uint8_t *brg_array, size_t num); #endif diff --git a/firmware/src/save.c b/firmware/src/save.c index 01365e0..d3b227e 100644 --- a/firmware/src/save.c +++ b/firmware/src/save.c @@ -1,8 +1,8 @@ /* - * Controller Save Save and Load + * Controller Config Save and Load * WHowe * - * Save is stored in last sector of flash + * Config is stored in last sector of flash */ #include "save.h" @@ -18,7 +18,7 @@ #include "pico/stdio.h" #include "hardware/flash.h" -#include "hardware/sync.h" +#include "pico/multicore.h" static struct { size_t size; @@ -27,7 +27,10 @@ static struct { } modules[8] = {0}; static int module_num = 0; -#define SAVE_PAGE_MAGIC 0x13424321 +static uint32_t my_magic = 0xcafecafe; + +#define SAVE_TIMEOUT_US 5000000 + #define SAVE_SECTOR_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE) typedef struct __attribute ((packed)) { @@ -43,30 +46,34 @@ static int data_page = -1; static bool requesting_save = false; static uint64_t requesting_time = 0; -static io_locker_func io_lock; +static mutex_t *io_lock; static void save_program() { old_data = new_data; data_page = (data_page + 1) % (FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE); - printf("Program Flash %d %8lx\n", data_page, old_data.magic); - io_lock(true); - uint32_t ints = save_and_disable_interrupts(); - if (data_page == 0) { - flash_range_erase(SAVE_SECTOR_OFFSET, FLASH_SECTOR_SIZE); + printf("\nProgram Flash %d %8lx\n", data_page, old_data.magic); + if (mutex_enter_timeout_us(io_lock, 100000)) { + sleep_ms(10); /* wait for all io operations to finish */ + uint32_t ints = save_and_disable_interrupts(); + if (data_page == 0) { + flash_range_erase(SAVE_SECTOR_OFFSET, FLASH_SECTOR_SIZE); + } + flash_range_program(SAVE_SECTOR_OFFSET + data_page * FLASH_PAGE_SIZE, + (uint8_t *)&old_data, FLASH_PAGE_SIZE); + restore_interrupts(ints); + mutex_exit(io_lock); + } else { + printf("Program Flash Failed.\n"); } - flash_range_program(SAVE_SECTOR_OFFSET + data_page * FLASH_PAGE_SIZE, - (uint8_t *)&old_data, FLASH_PAGE_SIZE); - restore_interrupts(ints); - io_lock(false); } static void load_default() { printf("Load Default\n"); new_data = default_data; - new_data.magic = SAVE_PAGE_MAGIC; + new_data.magic = my_magic; } static const page_t *get_page(int id) @@ -78,7 +85,7 @@ static const page_t *get_page(int id) static void save_load() { for (int i = 0; i < FLASH_SECTOR_SIZE / FLASH_PAGE_SIZE; i++) { - if (get_page(i)->magic != SAVE_PAGE_MAGIC) { + if (get_page(i)->magic != my_magic) { break; } data_page = i; @@ -102,8 +109,9 @@ static void save_loaded() } } -void save_init(io_locker_func locker) +void save_init(uint32_t magic, mutex_t *locker) { + my_magic = magic; io_lock = locker; save_load(); save_loop(); @@ -112,17 +120,13 @@ void save_init(io_locker_func locker) void save_loop() { - if (requesting_save && (time_us_64() - requesting_time > 1000000)) { + if (requesting_save && (time_us_64() - requesting_time > SAVE_TIMEOUT_US)) { requesting_save = false; - printf("Time to save.\n"); /* only when data is actually changed */ - for (int i = 0; i < sizeof(old_data); i++) { - if (((uint8_t *)&old_data)[i] != ((uint8_t *)&new_data)[i]) { - save_program(); - return; - } + if (memcmp(&old_data, &new_data, sizeof(old_data)) == 0) { + return; } - printf("No change.\n"); + save_program(); } } @@ -140,9 +144,9 @@ void *save_alloc(size_t size, void *def, void (*after_load)()) void save_request(bool immediately) { if (!requesting_save) { - printf("Save marked.\n"); + printf("Save requested.\n"); requesting_save = true; - new_data.magic = SAVE_PAGE_MAGIC; + new_data.magic = my_magic; requesting_time = time_us_64(); } if (immediately) { diff --git a/firmware/src/save.h b/firmware/src/save.h index e1b9ae6..2cf5f1b 100644 --- a/firmware/src/save.h +++ b/firmware/src/save.h @@ -7,11 +7,14 @@ #define SAVE_H #include +#include #include +#include "pico/multicore.h" + /* It's safer to lock other I/O ops during saving, so we need a locker */ typedef void (*io_locker_func)(bool pause); -void save_init(io_locker_func locker); +void save_init(uint32_t magic, mutex_t *lock); void save_loop(); diff --git a/firmware/src/setup.c b/firmware/src/setup.c deleted file mode 100644 index 6c39492..0000000 --- a/firmware/src/setup.c +++ /dev/null @@ -1,725 +0,0 @@ -/* - * Controller Setup Menu - * WHowe - * - * Setup is a mode, so one can change settings live - */ - -#include "setup.h" - -#include -#include -#include - -#include "bsp/board.h" -#include "pico/bootrom.h" - -#include "rgb.h" -#include "config.h" - -static iidx_cfg_t cfg_save; - -static uint64_t setup_tick_ms = 0; -#define CONCAT(a, b) a ## b -#define TVAR(line) CONCAT(a, line) -#define RUN_EVERY_N_MS(a, ms) { static uint64_t TVAR(__LINE__) = 0; \ - if (setup_tick_ms - TVAR(__LINE__) >= ms) { a; TVAR(__LINE__) = setup_tick_ms; } } -static uint32_t blink_fast = 0xffffffff; -static uint32_t blink_slow = 0xffffffff; - -uint32_t setup_led_tt[128]; -uint32_t setup_led_button[BUTTON_RGB_NUM]; - -typedef enum { - MODE_NONE, - MODE_TURNTABLE, - MODE_ANALOG, - MODE_LEVEL, - MODE_TT_THEME, - MODE_KEY_THEME, - MODE_KEY_OFF, - MODE_KEY_ON, -} setup_mode_t; -static setup_mode_t current_mode = MODE_NONE; - -static struct { - uint16_t last_keys; - uint16_t keys; - uint16_t just_pressed; - uint16_t just_released; - - int16_t last_angle; - int16_t angle; - int16_t rotate; -} input = { 0 }; - -#define KEY_1 0x0001 -#define KEY_2 0x0002 -#define KEY_3 0x0004 -#define KEY_4 0x0008 -#define KEY_5 0x0010 -#define KEY_6 0x0020 -#define KEY_7 0x0040 -#define E1 0x0080 -#define E2 0x0100 -#define E3 0x0200 -#define E4 0x0400 -#define AUX_NO 0x0800 -#define AUX_YES 0x1000 - -#define LED_KEY_1 0 -#define LED_KEY_2 1 -#define LED_KEY_3 2 -#define LED_KEY_4 3 -#define LED_KEY_5 4 -#define LED_KEY_6 5 -#define LED_KEY_7 6 -#define LED_E1 7 -#define LED_E2 8 -#define LED_E3 9 -#define LED_E4 10 - -#define PRESSED_ALL(k) ((input.keys & (k)) == (k)) -#define PRESSED_ANY(k) (input.keys & (k)) -#define JUST_PRESSED(k) (input.just_pressed & (k)) -#define JUST_RELEASED(k) (input.just_released & (k)) - -#define RED button_rgb32(99, 0, 0, false) -#define GREEN button_rgb32(0, 99, 0, false) -#define CYAN button_rgb32(0, 40, 99, false) -#define YELLOW button_rgb32(99, 99, 0, false) -#define SILVER button_rgb32(60, 60, 60, false) - -#define TT_RED tt_rgb32(99, 0, 0, false) -#define TT_GREEN tt_rgb32(0, 99, 0, false) -#define TT_CYAN tt_rgb32(0, 40, 99, false) -#define TT_YELLOW tt_rgb32(99, 99, 0, false) -#define TT_SILVER tt_rgb32(60, 60, 60, false) - -typedef void (*mode_func)(); - -static void join_mode(setup_mode_t new_mode); -static void quit_mode(bool apply); - -static void nop() -{ -} - -static void check_exit() -{ - if (JUST_PRESSED(AUX_YES)) { - quit_mode(true); - } else if (JUST_PRESSED(AUX_NO)) { - quit_mode(false); - } -} - -static int16_t input_delta(int16_t start_angle) -{ - int16_t delta = input.angle - start_angle; - if (delta > 128) { - delta -= 256; - } - if (delta < -128) { - delta += 256; - } - return delta; -} - -static setup_mode_t key_to_mode[11] = { - MODE_KEY_THEME, MODE_TT_THEME, MODE_KEY_ON, MODE_KEY_OFF, - MODE_NONE, MODE_NONE, MODE_NONE, - MODE_ANALOG, MODE_ANALOG, MODE_ANALOG, MODE_ANALOG, -}; - -static struct { - bool escaped; - uint64_t escape_time; - uint16_t start_angle; -} none_ctx = { 0 }; - -static void none_rotate() -{ - if (!none_ctx.escaped) { - return; - } - - int16_t delta = input_delta(none_ctx.start_angle); - if (abs(delta) > 10) { - join_mode(MODE_LEVEL); - none_ctx.escaped = false; - } -} - -static void none_loop() -{ - if (PRESSED_ALL(AUX_YES | AUX_NO)) { - if (!none_ctx.escaped) { - none_ctx.escaped = true; - none_ctx.escape_time = time_us_64(); - none_ctx.start_angle = input.angle; - } - } else { - none_ctx.escaped = false; - } - - if (!none_ctx.escaped) { - return; - } - - for (int i = 0; i < 11; i++) { - if (PRESSED_ANY(KEY_1 << i)) { - none_ctx.escaped = false; - join_mode(key_to_mode[i]); - return; - } - } - - if (time_us_64() - none_ctx.escape_time > 5000000) { - none_ctx.escaped = false; - join_mode(MODE_TURNTABLE); - return; - } -} - -static struct { - uint8_t adjust_led; /* 0: nothing, 1: adjust start, 2: adjust stop */ - int16_t start_angle; -} tt_ctx; - -static void tt_enter() -{ - tt_ctx.start_angle = input.angle; -} - -static void tt_key_change() -{ - if (JUST_PRESSED(E1)) { - tt_ctx.adjust_led = (tt_ctx.adjust_led == 1) ? 0 : 1; - tt_ctx.start_angle = input.angle; - } else if (JUST_PRESSED(E2)) { - tt_ctx.adjust_led = (tt_ctx.adjust_led == 2) ? 0 : 2; - tt_ctx.start_angle = input.angle; - } else if (JUST_PRESSED(E3)) { - iidx_cfg->tt_led.mode = (iidx_cfg->tt_led.mode + 1) % 3; - } else if (JUST_PRESSED(E4)) { - iidx_cfg->tt_sensor.mode = (iidx_cfg->tt_sensor.mode + 1) % 4; - } else if (JUST_PRESSED(KEY_2)) { - iidx_cfg->tt_sensor.deadzone = 0; - } else if (JUST_PRESSED(KEY_4)) { - iidx_cfg->tt_sensor.deadzone = 1; - } else if (JUST_PRESSED(KEY_6)) { - iidx_cfg->tt_sensor.deadzone = 2; - } else if (JUST_PRESSED(KEY_1)) { - iidx_cfg->tt_sensor.ppr = 0; - } else if (JUST_PRESSED(KEY_3)) { - iidx_cfg->tt_sensor.ppr = 1; - } else if (JUST_PRESSED(KEY_5)) { - iidx_cfg->tt_sensor.ppr = 2; - } else if (JUST_PRESSED(KEY_7)) { - iidx_cfg->tt_sensor.ppr = 3; - } - - check_exit(); -} - -static void tt_rotate() -{ - int16_t delta = input_delta(tt_ctx.start_angle); - if (abs(delta) > 8) { - tt_ctx.start_angle = input.angle; - - #define LED_START iidx_cfg->tt_led.start - #define LED_NUM iidx_cfg->tt_led.num - - if (tt_ctx.adjust_led == 1) { - if ((delta > 0) & (LED_START < 8)) { - LED_START++; - if (LED_NUM > 1) { - LED_NUM--; - } - } else if ((delta < 0) & (LED_START > 0)) { - LED_START--; - LED_NUM++; - } - } else if (tt_ctx.adjust_led == 2) { - if ((delta > 0) & (LED_NUM + LED_START < 128)) { - LED_NUM++; - } else if ((delta < 0) & (LED_NUM > 1)) { // at least 1 led - LED_NUM--; - } - } - } -} - -static void tt_loop() -{ - for (int i = 1; i < iidx_cfg->tt_led.num - 1; i++) { - setup_led_tt[i] = tt_rgb32(10, 10, 10, false); - } - - bool led_reversed = (iidx_cfg->tt_led.mode == 1); - int head = led_reversed ? TT_LED_NUM - 1 : 0; - int tail = led_reversed ? 0 : TT_LED_NUM - 1; - - setup_led_tt[head] = tt_rgb32(0xa0, 0, 0, false); - setup_led_tt[tail] = tt_rgb32(0, 0xa0, 0, false); - setup_led_button[LED_E2] = button_rgb32(0, 10, 0, false); - setup_led_button[LED_E1] = button_rgb32(10, 0, 0, false); - - if (tt_ctx.adjust_led == 1) { - setup_led_tt[head] &= blink_fast; - setup_led_button[LED_E1] = RED & blink_fast; - } else if (tt_ctx.adjust_led == 2) { - setup_led_tt[tail] &= blink_fast; - setup_led_button[LED_E2] = GREEN & blink_fast; - } - - switch (iidx_cfg->tt_led.mode) { - case 0: - setup_led_button[LED_E3] = GREEN; - break; - case 1: - setup_led_button[LED_E3] = RED; - break; - default: - setup_led_button[LED_E3] = 0; - break; - } - - switch (iidx_cfg->tt_sensor.mode) { - case 0: - setup_led_button[LED_E4] = GREEN; - break; - case 1: - setup_led_button[LED_E4] = RED; - break; - case 2: - setup_led_button[LED_E4] = CYAN; - break; - default: - setup_led_button[LED_E4] = YELLOW; - break; - } - setup_led_button[LED_KEY_2] = iidx_cfg->tt_sensor.deadzone == 0 ? SILVER : 0; - setup_led_button[LED_KEY_4] = iidx_cfg->tt_sensor.deadzone == 1 ? SILVER : 0; - setup_led_button[LED_KEY_6] = iidx_cfg->tt_sensor.deadzone == 2 ? SILVER : 0; - setup_led_button[LED_KEY_1] = iidx_cfg->tt_sensor.ppr == 0 ? SILVER : 0; - setup_led_button[LED_KEY_3] = iidx_cfg->tt_sensor.ppr == 1 ? SILVER : 0; - setup_led_button[LED_KEY_5] = iidx_cfg->tt_sensor.ppr == 2 ? SILVER : 0; - setup_led_button[LED_KEY_7] = iidx_cfg->tt_sensor.ppr == 3 ? SILVER : 0; -} - -static void level_rotate() -{ - int16_t new_value = iidx_cfg->level; - new_value += input.rotate; - if (new_value < 0) { - new_value = 0; - } else if (new_value > 255) { - new_value = 255; - } - iidx_cfg->level = new_value; - printf("Level: %d\n", iidx_cfg->level); -} - -static void level_key_change() -{ - if (JUST_PRESSED(KEY_1)) { - iidx_cfg->level = 0; - } else if (JUST_PRESSED(KEY_2)) { - iidx_cfg->level = 20; - } else if (JUST_PRESSED(KEY_3)) { - iidx_cfg->level = 50; - } else if (JUST_PRESSED(KEY_4)) { - iidx_cfg->level = 85; - } else if (JUST_PRESSED(KEY_5)) { - iidx_cfg->level = 130; - } else if (JUST_PRESSED(KEY_6)) { - iidx_cfg->level = 190; - } else if (JUST_PRESSED(KEY_7)) { - iidx_cfg->level = 255; - } - - check_exit(); -} - -static void level_loop() -{ - for (int i = 0; i < 7; i++) { - hsv_t key_color = {i * 255 / 7, 255, 255 }; - setup_led_button[i] = button_hsv(key_color); - } - - uint16_t pos = iidx_cfg->level * iidx_cfg->tt_led.num / 256; - for (unsigned i = 0; i < iidx_cfg->tt_led.num; i++) { - setup_led_tt[i] = (i == pos) ? tt_rgb32(90, 90, 90, false) : 0; - } -} - -static struct { - uint8_t channel; /* 0:E1(Start), 1:E2(Effect), 2:E3(VEFX), 3:E4 */ - volatile uint8_t *value; - int16_t start_angle; -} analog_ctx; - -static void analog_key_change() -{ - if (JUST_PRESSED(E1)) { - analog_ctx.channel = 0; - analog_ctx.value = &iidx_cfg->effects.e1; - } else if (JUST_PRESSED(E2)) { - analog_ctx.channel = 1; - analog_ctx.value = &iidx_cfg->effects.e2; - } else if (JUST_PRESSED(E3)) { - analog_ctx.channel = 2; - analog_ctx.value = &iidx_cfg->effects.e3; - } else if (JUST_PRESSED(E4)) { - analog_ctx.channel = 3; - analog_ctx.value = &iidx_cfg->effects.e4; - } else if (JUST_PRESSED(KEY_1)) { - *analog_ctx.value = 0; - } else if (JUST_PRESSED(KEY_2)) { - *analog_ctx.value = 43; - } else if (JUST_PRESSED(KEY_3)) { - *analog_ctx.value = 85; - } else if (JUST_PRESSED(KEY_4)) { - *analog_ctx.value = 128; - } else if (JUST_PRESSED(KEY_5)) { - *analog_ctx.value = 170; - } else if (JUST_PRESSED(KEY_6)) { - *analog_ctx.value = 213; - } else if (JUST_PRESSED(KEY_7)) { - *analog_ctx.value = 255; - } - - check_exit(); -} - -static void analog_enter() -{ - analog_key_change(); -} - -static void analog_rotate() -{ - int16_t new_value = *analog_ctx.value; - new_value += input.rotate; - if (new_value < 0) { - new_value = 0; - } else if (new_value > 255) { - new_value = 255; - } - *analog_ctx.value = new_value; -} - -static uint32_t scale_color(uint32_t color, uint8_t value, uint8_t factor) -{ - uint8_t r = (color >> 16) & 0xff; - uint8_t g = (color >> 8) & 0xff; - uint8_t b = color & 0xff; - - r = (r * value) / factor; - g = (g * value) / factor; - b = (b * value) / factor; - - return (r << 16) | (g << 8) | b; -} - -static void analog_loop() -{ - uint32_t colors[4] = { RED, GREEN, CYAN, YELLOW}; - uint32_t tt_colors[4] = { TT_RED, TT_GREEN, TT_CYAN, TT_YELLOW }; - - for (int i = 0; i < 4; i++) { - uint32_t color = colors[i]; - if (analog_ctx.channel == i) { - color &= blink_fast; - } - setup_led_button[LED_E1 + i] = color; - } - - int tt_split = (int)*analog_ctx.value * iidx_cfg->tt_led.num / 255; - - for (int i = 1; i < iidx_cfg->tt_led.num - 1; i++) { - setup_led_tt[i] = i < tt_split ? tt_colors[analog_ctx.channel] : 0; - } - - int button_split = *analog_ctx.value / 37; - int scale = *analog_ctx.value % 37; - for (int i = 0; i < 7; i++) { - uint32_t color = colors[analog_ctx.channel]; - if (i == button_split) { - color = scale_color(color, scale, 37); - } else if (i > button_split) { - color = 0; - } - setup_led_button[LED_KEY_1 + i] = color; - } -} - -static struct { - uint8_t phase; /* 0:H, 1:S, 2:V */ - hsv_t hsv; - uint8_t *value; - int16_t start_angle; - uint16_t keys; - hsv_t *leds; -} key_ctx; - -static void key_apply() -{ - for (int i = 0; i < 11; i++) { - if (key_ctx.keys & (1 << i)) { - key_ctx.leds[i] = key_ctx.hsv; - } - } -} - -static void key_change() -{ - if (JUST_PRESSED(AUX_NO)) { - quit_mode(false); - return; - } - - if (JUST_PRESSED(AUX_YES)) { - key_ctx.phase++; - if (key_ctx.phase == 3) { - key_apply(); - quit_mode(true); - return; - } - - if (key_ctx.phase == 1) { - key_ctx.value = &key_ctx.hsv.s; - } else { - key_ctx.value = &key_ctx.hsv.v; - } - return; - } - - key_ctx.keys ^= input.just_pressed; -} - -static void key_rotate() -{ - int16_t new_value = *key_ctx.value; - new_value += input.rotate; - if (key_ctx.phase > 0) { - if (new_value < 0) { - new_value = 0; - } else if (new_value > 255) { - new_value = 255; - } - } - *key_ctx.value = (uint8_t)new_value; -} - -static void key_loop() -{ - for (int i = 0; i < 11; i ++) { - if (key_ctx.keys == 0) { - setup_led_button[i] = button_hsv(key_ctx.hsv) & blink_slow; - } else if (key_ctx.keys & (1 << i)) { - setup_led_button[i] = button_hsv(key_ctx.hsv); - } else { - setup_led_button[i] = 0; - } - } - - uint16_t pos = *key_ctx.value * iidx_cfg->tt_led.num / 256; - for (unsigned i = 0; i < iidx_cfg->tt_led.num; i++) { - setup_led_tt[i] = (i == pos) ? tt_rgb32(90, 90, 90, false) : 0; - } -} - -static void key_enter() -{ - key_ctx = (typeof(key_ctx)) { - .phase = 0, - .hsv = { .h = 200, .s = 255, .v = 128 }, - .value = &key_ctx.hsv.h, - .start_angle = input.angle, - .keys = 0, - .leds = iidx_cfg->key_on, - }; - - if (current_mode == MODE_KEY_OFF) { - key_ctx.hsv = (hsv_t) { .h = 60, .s = 255, .v = 5 }; - key_ctx.leds = iidx_cfg->key_off; - } -} - -#define K0_WHITE {.v = 5} -#define K0_SKY {.h = 215, .s = 255, .v = 20} -#define K0_RED {.h = 87, .s = 255, .v = 20} -#define K0_GREEN {.h = 0, .s = 255, .v = 20} - -#define K1_WHITE {.v = 200} -#define K1_SKY {.h = 215, .s = 255, .v = 230} -#define K1_RED {.h = 87, .s = 255, .v = 230} -#define K1_GREEN {.h = 0, .s = 255, .v = 230} - -#define K0_RAINBOW(x) {.h = 23 * x, .s = 255, .v = 20} -#define K1_RAINBOW(x) {.h = 23 * x, .s = 255, .v = 230} - -static struct { - hsv_t key_off[11]; - hsv_t key_on[11]; -} themes[7] = { - {{ { 0 }, }, - { K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, - K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE }, - }, - {{ { 0 }, }, - { K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, - K1_RED, K1_GREEN, K1_GREEN, K1_SKY }, - }, - {{ { 0 }, }, - { K1_RED, K1_SKY, K1_RED, K1_SKY, K1_RED, K1_SKY, K1_RED, - K1_RED, K1_GREEN, K1_GREEN, K1_GREEN }, - }, - {{ K0_WHITE, K0_WHITE, K0_WHITE, K0_WHITE, K0_WHITE, K0_WHITE, K0_WHITE, - K0_WHITE, K0_WHITE, K0_WHITE, K0_WHITE }, - { K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, - K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE }, - }, - {{ K0_RED, K0_SKY, K0_RED, K0_SKY, K0_RED, K0_SKY, K0_RED, - K0_RED, K0_GREEN, K0_GREEN, K0_GREEN }, - { K1_RED, K1_SKY, K1_RED, K1_SKY, K1_RED, K1_SKY, K1_RED, - K1_RED, K1_GREEN, K1_GREEN, K1_GREEN }, - }, - {{ K0_RAINBOW(0), K0_RAINBOW(1), K0_RAINBOW(2), K0_RAINBOW(3), - K0_RAINBOW(4), K0_RAINBOW(5), K0_RAINBOW(6), - K0_RAINBOW(7), K0_RAINBOW(8), K0_RAINBOW(9), K0_RAINBOW(10) - }, - { K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE, - K1_WHITE, K1_WHITE, K1_WHITE, K1_WHITE }, - }, - {{ K0_RAINBOW(0), K0_RAINBOW(1), K0_RAINBOW(2), K0_RAINBOW(3), - K0_RAINBOW(4), K0_RAINBOW(5), K0_RAINBOW(6), - K0_RAINBOW(7), K0_RAINBOW(8), K0_RAINBOW(9), K0_RAINBOW(10) - }, - { K1_RAINBOW(0), K1_RAINBOW(1), K1_RAINBOW(2), K1_RAINBOW(3), - K1_RAINBOW(4), K1_RAINBOW(5), K1_RAINBOW(6), - K1_RAINBOW(7), K1_RAINBOW(8), K1_RAINBOW(9), K1_RAINBOW(10) - } - }, -}; - -static void key_theme_key_change() -{ - for (int i = 0; i < 7; i++) { - if (JUST_PRESSED(KEY_1 << i)) { - memcpy(iidx_cfg->key_off, themes[i].key_off, sizeof(iidx_cfg->key_off)); - memcpy(iidx_cfg->key_on, themes[i].key_on, sizeof(iidx_cfg->key_on)); - break; - } - } - check_exit(); -} - -static void key_theme_loop() -{ - for (int i = 0; i < 11; i++) { - if (blink_slow) { - setup_led_button[i] = button_hsv(iidx_cfg->key_on[i]); - } else { - setup_led_button[i] = button_hsv(iidx_cfg->key_off[i]); - } - } -} - -static void tt_theme_key_change() -{ - for (int i = 0; i < 7; i++) { - if (JUST_PRESSED(KEY_1 << i)) { - iidx_cfg->tt_led.effect = i; - break; - } - } - check_exit(); -} - -static void tt_theme_loop() -{ - for (int i = 0; i < 7; i++) { - setup_led_button[i] = iidx_cfg->tt_led.effect == i ? SILVER : 0; - } -} - -static struct { - mode_func key_change; - mode_func rotate; - mode_func loop; - mode_func enter; -} mode_defs[] = { - [MODE_NONE] = { nop, none_rotate, none_loop, nop}, - [MODE_TURNTABLE] = { tt_key_change, tt_rotate, tt_loop, tt_enter}, - [MODE_ANALOG] = { analog_key_change, analog_rotate, analog_loop, analog_enter}, - [MODE_LEVEL] = { level_key_change, level_rotate, level_loop, nop}, - [MODE_TT_THEME] = { tt_theme_key_change, nop, tt_theme_loop, nop}, - [MODE_KEY_THEME] = { key_theme_key_change, nop, key_theme_loop, nop}, - [MODE_KEY_OFF] = { key_change, key_rotate, key_loop, key_enter}, - [MODE_KEY_ON] = { key_change, key_rotate, key_loop, key_enter}, -}; - -static void join_mode(setup_mode_t new_mode) -{ - cfg_save = *iidx_cfg; - memset(&setup_led_tt, 0, sizeof(setup_led_tt)); - memset(&setup_led_button, 0, sizeof(setup_led_button)); - current_mode = new_mode; - mode_defs[current_mode].enter(); - printf("Entering setup %d\n", new_mode); -} - -static void quit_mode(bool apply) -{ - if (apply) { - config_changed(); - } else { - *iidx_cfg = cfg_save; - } - current_mode = MODE_NONE; - printf("Quit setup %s\n", apply ? "saved." : "discarded."); -} - -bool setup_run(uint16_t keys, uint16_t angle) -{ - setup_tick_ms = time_us_64() / 1000; - input.keys = keys; - input.angle = angle; - input.just_pressed = keys & ~input.last_keys; - input.just_released = ~keys & input.last_keys; - input.rotate = input_delta(input.last_angle); - if (input.rotate != 0) { - printf("@ %3d %2x\n", input.rotate, input.angle); - mode_defs[current_mode].rotate(); - } - if (input.just_pressed) { - printf("+ %04x\n", input.just_pressed); - } - if (input.just_released) { - printf("- %04x\n", input.just_released); - } - - if (input.just_pressed || input.just_released) { - mode_defs[current_mode].key_change(); - } - - RUN_EVERY_N_MS(blink_fast = ~blink_fast, 100); - RUN_EVERY_N_MS(blink_slow = ~blink_slow, 500); - - mode_defs[current_mode].loop(); - - input.last_keys = keys; - input.last_angle = angle; - - return current_mode != MODE_NONE; -} - -void setup_init() -{ -} \ No newline at end of file diff --git a/firmware/src/setup.h b/firmware/src/setup.h deleted file mode 100644 index cf00854..0000000 --- a/firmware/src/setup.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Controller Setup - * WHowe - */ - -#ifndef SETUP_H -#define SETUP_H - -#include -#include -#include "board_defs.h" - -void setup_init(); -bool setup_run(uint16_t key_flag, uint16_t tt_angle); - -extern uint32_t setup_led_tt[]; -extern uint32_t setup_led_button[BUTTON_RGB_NUM]; - -#endif diff --git a/firmware/src/touch.c b/firmware/src/touch.c new file mode 100644 index 0000000..3b2b6a2 --- /dev/null +++ b/firmware/src/touch.c @@ -0,0 +1,69 @@ +/* + * Mai Pico Silder Keys + * WHowe + * + * MPR121 CapSense based Keys + */ + +#include "touch.h" + +#include +#include +#include + +#include "bsp/board.h" +#include "hardware/gpio.h" +#include "hardware/i2c.h" + +#include "board_defs.h" + +#include "config.h" +#include "mpr121.h" + +#define MPR121_ADDR 0x5A + +static uint16_t baseline[36]; +static int16_t error[36]; +static uint16_t readout[36]; +static bool touched[36]; +static uint16_t touch[3]; + +void touch_init() +{ + i2c_init(I2C_PORT, I2C_FREQ); + gpio_set_function(I2C_SDA, GPIO_FUNC_I2C); + gpio_set_function(I2C_SCL, GPIO_FUNC_I2C); + gpio_pull_up(I2C_SDA); + gpio_pull_up(I2C_SCL); + + for (int m = 0; m < 3; m++) { + mpr121_init(MPR121_ADDR + m); + } + touch_update_config(); +} + +void touch_update() +{ + touch[0] = mpr121_touched(MPR121_ADDR); + touch[1] = mpr121_touched(MPR121_ADDR + 1); + touch[2] = mpr121_touched(MPR121_ADDR + 2); +} + +bool touch_touched(unsigned key) +{ + if (key >= 32) { + return 0; + } + return touch[key / 12] & (1 << (key % 12)); +} + +void touch_update_config() +{ + for (int m = 0; m < 3; m++) { + mpr121_debounce(MPR121_ADDR + m, mai_cfg->sense.debounce_touch, + mai_cfg->sense.debounce_release); + mpr121_sense(MPR121_ADDR + m, mai_cfg->sense.global, mai_cfg->sense.keys + m * 12); + mpr121_filter(MPR121_ADDR + m, mai_cfg->sense.filter & 0x0f, + (mai_cfg->sense.filter >> 4) & 0x0f); + } +} diff --git a/firmware/src/touch.h b/firmware/src/touch.h new file mode 100644 index 0000000..39ffd63 --- /dev/null +++ b/firmware/src/touch.h @@ -0,0 +1,17 @@ +/* + * Mai Pico Touch Keys + * WHowe + */ + +#ifndef TOUCH_H +#define TOUCH_H + +#include +#include + +void touch_init(); +void touch_update(); +bool touch_touched(unsigned key); +void touch_update_config(); + +#endif diff --git a/firmware/src/tusb_config.h b/firmware/src/tusb_config.h index e7a9ac6..9f81ac2 100644 --- a/firmware/src/tusb_config.h +++ b/firmware/src/tusb_config.h @@ -96,7 +96,7 @@ extern "C" { #endif //------------- CLASS -------------// -#define CFG_TUD_HID 1 +#define CFG_TUD_HID 3 #define CFG_TUD_CDC 1 #define CFG_TUD_MSC 0 #define CFG_TUD_MIDI 0 diff --git a/firmware/src/usb_descriptors.c b/firmware/src/usb_descriptors.c index 203f61e..12ee53a 100644 --- a/firmware/src/usb_descriptors.c +++ b/firmware/src/usb_descriptors.c @@ -36,8 +36,8 @@ */ #define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n)) #define USB_PID \ - (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ - _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4)) + (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4)) //--------------------------------------------------------------------+ // Device Descriptors @@ -51,8 +51,11 @@ tusb_desc_device_t desc_device_joy = { .bDeviceProtocol = 0x00, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .idVendor = 0xCaff, - .idProduct = USB_PID, +// To match CrazyRedMachine dll +// vid 0x0f0d, pid 0x0092, interface 1 + + .idVendor = 0x0f0d, + .idProduct = 0x0092, .bcdDevice = 0x0100, .iManufacturer = 0x01, @@ -64,7 +67,7 @@ tusb_desc_device_t desc_device_joy = { // Invoked when received GET DEVICE DESCRIPTOR // Application return pointer to descriptor uint8_t const* tud_descriptor_device_cb(void) { - return (uint8_t const*)&desc_device_joy; + return (uint8_t const*)&desc_device_joy; } //--------------------------------------------------------------------+ @@ -72,27 +75,49 @@ uint8_t const* tud_descriptor_device_cb(void) { //--------------------------------------------------------------------+ uint8_t const desc_hid_report_joy[] = { - GAMECON_REPORT_DESC_JOYSTICK(HID_REPORT_ID(REPORT_ID_JOYSTICK)), - GAMECON_REPORT_DESC_LIGHTS(HID_REPORT_ID(REPORT_ID_LIGHTS)) + MAIPICO_REPORT_DESC_JOYSTICK, +}; + +uint8_t const desc_hid_report_led[] = { + MAIPICO_LED_HEADER, + MAIPICO_REPORT_DESC_LED_touch_16, + MAIPICO_REPORT_DESC_LED_touch_15, + MAIPICO_REPORT_DESC_LED_TOWER_6, + MAIPICO_REPORT_DESC_LED_COMPRESSED, + MAIPICO_LED_FOOTER +}; + +uint8_t const desc_hid_report_nkro[] = { + MAIPICO_REPORT_DESC_NKRO, }; // Invoked when received GET HID REPORT DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete -uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf) { - (void)itf; - return desc_hid_report_joy; +uint8_t const* tud_hid_descriptor_report_cb(uint8_t itf) +{ + switch (itf) { + case 0: + return desc_hid_report_joy; + case 1: + return desc_hid_report_led; + case 2: + return desc_hid_report_nkro; + default: + return NULL; + } } - //--------------------------------------------------------------------+ // Configuration Descriptor //--------------------------------------------------------------------+ -enum { ITF_NUM_HID, ITF_NUM_CDC, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL }; +enum { ITF_NUM_JOY, ITF_NUM_LED, ITF_NUM_NKRO, ITF_NUM_CDC, ITF_NUM_CDC_DATA, ITF_NUM_TOTAL }; -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN + TUD_CDC_DESC_LEN) +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN * 3 + TUD_CDC_DESC_LEN) -#define EPNUM_HID 0x84 +#define EPNUM_JOY 0x84 +#define EPNUM_LED 0x85 +#define EPNUM_KEY 0x86 #define EPNUM_CDC_NOTIF 0x81 #define EPNUM_CDC_OUT 0x02 #define EPNUM_CDC_IN 0x82 @@ -101,25 +126,31 @@ uint8_t const desc_configuration_joy[] = { // Config number, interface count, string index, total length, attribute, // power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, - TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 200), // Interface number, string index, protocol, report descriptor len, EP In // address, size & polling interval - TUD_HID_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, - sizeof(desc_hid_report_joy), EPNUM_HID, + TUD_HID_DESCRIPTOR(ITF_NUM_JOY, 4, HID_ITF_PROTOCOL_NONE, + sizeof(desc_hid_report_joy), EPNUM_JOY, CFG_TUD_HID_EP_BUFSIZE, 1), - - TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, - 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64) - }; + TUD_HID_DESCRIPTOR(ITF_NUM_LED, 5, HID_ITF_PROTOCOL_NONE, + sizeof(desc_hid_report_led), EPNUM_LED, + CFG_TUD_HID_EP_BUFSIZE, 4), + + TUD_HID_DESCRIPTOR(ITF_NUM_NKRO, 6, HID_ITF_PROTOCOL_NONE, + sizeof(desc_hid_report_nkro), EPNUM_KEY, + CFG_TUD_HID_EP_BUFSIZE, 1), + + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 7, EPNUM_CDC_NOTIF, + 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64) +}; // Invoked when received GET CONFIGURATION DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete uint8_t const* tud_descriptor_configuration_cb(uint8_t index) { - (void)index; // for multiple configurations - return desc_configuration_joy; + return desc_configuration_joy; } //--------------------------------------------------------------------+ @@ -130,66 +161,61 @@ uint8_t const* tud_descriptor_configuration_cb(uint8_t index) { const char *string_desc_arr[] = { (const char[]){0x09, 0x04}, // 0: is supported language is English (0x0409) "WHowe" , // 1: Manufacturer - "Pico IIDX Controller", // 2: Product - "654321", // 3: Serials, should use chip ID - "Button 1", - "Button 2", - "Button 3", - "Button 4", - "Button 5", - "Button 6", - "Button 7", - "E1", - "E2", - "E3", - "E4" + "Mai Pico Controller", // 2: Product + "123456", // 3: Serials, should use chip ID + "Mai Pico Joystick", + "Mai Pico LED", + "Mai Pico NKRO", + "Mai Pico Serial Port", }; -static uint16_t _desc_str[64]; - // Invoked when received GET STRING DESCRIPTOR request // Application return pointer to descriptor, whose contents must exist long // enough for transfer to complete -uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - (void)langid; - - uint8_t chr_count; - - if (index == 0) { - memcpy(&_desc_str[1], string_desc_arr[0], 2); - chr_count = 1; - } else { - // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. - // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors - - if (index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) { - const char* str = string_desc_arr[index]; - - // Cap at max char - chr_count = strlen(str); - if (chr_count > 63) chr_count = 63; - - // Convert ASCII string into UTF-16 - for (uint8_t i = 0; i < chr_count; i++) { - _desc_str[1 + i] = str[i]; - } - } else { - _desc_str[1] = 'X'; - chr_count = 1; - } - } - - - // first byte is length (including header), second byte is string type - _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); - - return _desc_str; -} - -void konami_mode() +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { - desc_device_joy.idVendor = 0x1ccf; - desc_device_joy.idProduct = 0x8048; - string_desc_arr[1] = "Konami Amusement"; - string_desc_arr[2] = "beatmania IIDX controller premium model"; + static uint16_t _desc_str[64]; + + if (index == 0) { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 + 2); + return _desc_str; + } + + const size_t base_num = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]); + const char *colors[] = {"Blue", "Red", "Green"}; + char str[64]; + + if (index < base_num) { + strcpy(str, string_desc_arr[index]); + } else if (index < base_num + 48 + 45) { + const char *names[] = {"Key ", "Splitter "}; + int led = index - base_num; + int id = led / 6 + 1; + int type = led / 3 % 2; + int brg = led % 3; + sprintf(str, "%s%02d %s", names[type], id, colors[brg]); + } else if (index < base_num + 48 + 45 + 18) { + int led = index - base_num - 48 - 45; + int id = led / 3 + 1; + int brg = led % 3; + sprintf(str, "Tower %02d %s", id, colors[brg]); + } else { + sprintf(str, "Unknown %d", index); + } + + uint8_t chr_count = strlen(str); + if (chr_count > 63) { + chr_count = 63; + } + + // Convert ASCII string into UTF-16 + for (uint8_t i = 0; i < chr_count; i++) { + _desc_str[1 + i] = str[i]; + } + + // first byte is length (including header), second byte is string type + _desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * chr_count + 2); + + return _desc_str; } diff --git a/firmware/src/usb_descriptors.h b/firmware/src/usb_descriptors.h index c6021d8..cfc8029 100644 --- a/firmware/src/usb_descriptors.h +++ b/firmware/src/usb_descriptors.h @@ -5,10 +5,11 @@ #include "device/usbd.h" enum { - REPORT_ID_JOYSTICK = 1, - REPORT_ID_LIGHTS, - REPORT_ID_KEYBOARD, - REPORT_ID_MOUSE, + REPORT_ID_JOYSTICK = 1, + REPORT_ID_LED_touch_16 = 4, + REPORT_ID_LED_touch_15 = 5, + REPORT_ID_LED_TOWER_6 = 6, + REPORT_ID_LED_COMPRESSED = 11, }; // because they are missing from tusb_hid.h @@ -21,51 +22,120 @@ enum { // Joystick Report Descriptor Template - Based off Drewol/rp2040-gamecon // Button Map | X | Y -#define GAMECON_REPORT_DESC_JOYSTICK(...) \ - HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \ - HID_USAGE(HID_USAGE_DESKTOP_JOYSTICK), \ - HID_COLLECTION(HID_COLLECTION_APPLICATION), \ - __VA_ARGS__ HID_USAGE_PAGE(HID_USAGE_PAGE_BUTTON), HID_USAGE_MIN(1), \ - HID_USAGE_MAX(13), \ - HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), HID_REPORT_COUNT(13), \ - HID_REPORT_SIZE(1), HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ - HID_REPORT_COUNT(1), HID_REPORT_SIZE(16 - 13), /*Padding*/ \ - HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), \ - HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_LOGICAL_MIN(0x00), \ - HID_LOGICAL_MAX_N(0x00ff, 2), /* Below is Joystick/analog */ \ - HID_USAGE(HID_USAGE_DESKTOP_X), HID_USAGE(HID_USAGE_DESKTOP_Y), \ - HID_USAGE(HID_USAGE_DESKTOP_Z), HID_USAGE(HID_USAGE_DESKTOP_RX), \ - HID_USAGE(HID_USAGE_DESKTOP_RY), HID_USAGE(HID_USAGE_DESKTOP_RZ), \ - HID_REPORT_COUNT(6), HID_REPORT_SIZE(8), \ - HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), HID_COLLECTION_END +#define MAIPICO_REPORT_DESC_JOYSTICK \ + HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \ + HID_USAGE(HID_USAGE_DESKTOP_JOYSTICK), \ + HID_COLLECTION(HID_COLLECTION_APPLICATION), \ + HID_REPORT_ID(REPORT_ID_JOYSTICK) \ + HID_USAGE_PAGE(HID_USAGE_PAGE_BUTTON), \ + HID_USAGE_MIN(1), HID_USAGE_MAX(16), \ + HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \ + HID_REPORT_COUNT(16), HID_REPORT_SIZE(1), \ + HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ + \ + HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \ + HID_USAGE(HID_USAGE_DESKTOP_HAT_SWITCH), \ + HID_LOGICAL_MIN(1), HID_LOGICAL_MAX(8), \ + HID_PHYSICAL_MIN(0), HID_PHYSICAL_MAX_N(315, 2), \ + HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \ + HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ + \ + HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \ + HID_USAGE(HID_USAGE_DESKTOP_X), HID_USAGE(HID_USAGE_DESKTOP_Y), \ + HID_USAGE(HID_USAGE_DESKTOP_Z), HID_USAGE(HID_USAGE_DESKTOP_RX), \ + HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), /* Analog */ \ + HID_REPORT_SIZE(8), HID_REPORT_COUNT(4), \ + HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ + \ + HID_USAGE_PAGE_N(HID_USAGE_PAGE_VENDOR, 2), \ + HID_USAGE(0), \ + HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX(0xff), \ + HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), \ + HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ + HID_COLLECTION_END -// Light Map -#define GAMECON_REPORT_DESC_LIGHTS(...) \ - HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \ - HID_COLLECTION(HID_COLLECTION_APPLICATION), \ - __VA_ARGS__ HID_REPORT_COUNT(11), /* LED NUM */ \ - HID_REPORT_SIZE(8), HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \ - HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), HID_STRING_MINIMUM(4), \ - HID_STRING_MAXIMUM(16), HID_USAGE_MIN(1), HID_USAGE_MAX(16), \ - HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), HID_REPORT_COUNT(1), \ - HID_REPORT_SIZE(8), /*Padding*/ \ - HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), \ - HID_COLLECTION_END +#define MAIPICO_LED_HEADER \ + HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(0x00), \ + HID_COLLECTION(HID_COLLECTION_APPLICATION), \ + HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), \ + HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE) -// NKRO Descriptor -#define GAMECON_REPORT_DESC_NKRO(...) \ - HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(HID_USAGE_PAGE_KEYBOARD), \ - HID_COLLECTION(HID_COLLECTION_APPLICATION), \ - __VA_ARGS__ HID_REPORT_SIZE(1), HID_REPORT_COUNT(8), \ - HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), HID_USAGE_MIN(224), \ - HID_USAGE_MAX(231), HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \ - HID_INPUT(HID_VARIABLE), HID_REPORT_SIZE(1), HID_REPORT_COUNT(31 * 8), \ - HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \ - HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), HID_USAGE_MIN(0), \ - HID_USAGE_MAX(31 * 8 - 1), HID_INPUT(HID_VARIABLE), HID_COLLECTION_END +#define MAIPICO_LED_FOOTER \ + HID_COLLECTION_END +// Slider First 16 LEDs (48 rgb zones, BRG order) +#define MAIPICO_REPORT_DESC_LED_touch_16 \ + HID_REPORT_ID(REPORT_ID_LED_touch_16) \ + HID_REPORT_COUNT(48), HID_REPORT_SIZE(8), \ + HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \ + HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \ + HID_USAGE_MIN(1), HID_USAGE_MAX(48), \ + HID_STRING_MINIMUM(8), HID_STRING_MAXIMUM(55), \ + HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE) -/* Enable Konami spoof mode */ -void konami_mode(); +// Slider Remaining 15 LEDs (45 rgb zones, BRG order) +#define MAIPICO_REPORT_DESC_LED_touch_15 \ + HID_REPORT_ID(REPORT_ID_LED_touch_15) \ + HID_REPORT_COUNT(45), HID_REPORT_SIZE(8), \ + HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \ + HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \ + HID_USAGE_MIN(49), HID_USAGE_MAX(93), \ + HID_STRING_MINIMUM(8), HID_STRING_MAXIMUM(52), /* Delta to previous */ \ + HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE) -#endif /* USB_DESCRIPTORS_H_ */ \ No newline at end of file +// Tower LEDs (18 rgb zones, BRG order) +#define MAIPICO_REPORT_DESC_LED_TOWER_6 \ + HID_REPORT_ID(REPORT_ID_LED_TOWER_6) \ + HID_REPORT_COUNT(18), HID_REPORT_SIZE(8), \ + HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \ + HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \ + HID_USAGE_MIN(94), HID_USAGE_MAX(111), \ + HID_STRING_MINIMUM(8), HID_STRING_MAXIMUM(25), /* Delta to previous */ \ + HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE) + +// LEDs Compressed +#define MAIPICO_REPORT_DESC_LED_COMPRESSED \ + HID_REPORT_ID(REPORT_ID_LED_COMPRESSED) \ + HID_USAGE_PAGE(HID_USAGE_PAGE_ORDINAL), \ + HID_USAGE(0x00), \ + HID_LOGICAL_MIN(0x00), HID_LOGICAL_MAX_N(0x00ff, 2), \ + HID_REPORT_SIZE(8), HID_REPORT_COUNT(63), \ + HID_FEATURE(HID_DATA | HID_VARIABLE | HID_ABSOLUTE) + +#define MAIPICO_REPORT_DESC_NKRO \ + HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), \ + HID_USAGE(HID_USAGE_DESKTOP_KEYBOARD), \ + HID_COLLECTION(HID_COLLECTION_APPLICATION), \ + /* Modifier */ \ + HID_REPORT_SIZE(1), \ + HID_REPORT_COUNT(8), \ + HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), \ + HID_USAGE_MIN(224), \ + HID_USAGE_MAX(231), \ + HID_LOGICAL_MIN(0), \ + HID_LOGICAL_MAX(1), \ + HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ + /* LED output that we don't care */ \ + HID_REPORT_COUNT(5), \ + HID_REPORT_SIZE(1), \ + HID_USAGE_PAGE(HID_USAGE_PAGE_LED), \ + HID_USAGE_MIN(1), \ + HID_USAGE_MAX(5), \ + HID_OUTPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ + HID_REPORT_COUNT(1), \ + HID_REPORT_SIZE(3), \ + HID_OUTPUT(HID_CONSTANT), \ + /* Full Keyboard Bitmap */ \ + HID_REPORT_SIZE(1), \ + HID_REPORT_COUNT(120), \ + HID_LOGICAL_MIN(0), \ + HID_LOGICAL_MAX(1), \ + HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), \ + HID_USAGE_MIN(0), \ + HID_USAGE_MAX(119), \ + HID_INPUT(HID_DATA | HID_VARIABLE | HID_ABSOLUTE), \ + HID_COLLECTION_END + +// HID_REPORT_ID(REPORT_ID_NKRO) + +#endif /* USB_DESCRIPTORS_H_ */