Firmware copied from chu pico

This commit is contained in:
whowechina
2023-09-24 16:17:09 +08:00
parent 328d0b8209
commit 5eb5da42e2
29 changed files with 1285 additions and 2107 deletions
-1
View File
@@ -9,4 +9,3 @@ set(CMAKE_C_STANDARD 11)
pico_sdk_init()
add_subdirectory(src)
add_subdirectory(lib/pico-mpr121 mpr121)
-10
View File
@@ -1,10 +0,0 @@
# IIDX Pico Firmware
Features:
* 1000Hz polling rate.
* HID lights.
* RGB turntable.
* Configuration save.
* Customizable through board_defs.h
* Dymanic settings.
-17
View File
@@ -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
)
-363
View File
@@ -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, &reg, 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, &reg, 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
-170
View File
@@ -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);
}
Binary file not shown.
+5 -5
View File
@@ -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)
+9 -22
View File
@@ -1,32 +1,19 @@
/*
* IIDX Controller Board Definitions
* Mai Controller Board Definitions
* WHowe <github.com/whowechina>
*/
#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
-73
View File
@@ -1,73 +0,0 @@
/*
* Controller Buttons
* WHowe <github.com/whowechina>
*
* A button consists of a switch and an LED
*/
#include "buttons.h"
#include <stdint.h>
#include <stdbool.h>
#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;
}
-17
View File
@@ -1,17 +0,0 @@
/*
* Controller Buttons
* WHowe <github.com/whowechina>
*/
#ifndef BUTTONS_H
#define BUTTONS_H
#include <stdint.h>
void button_init();
uint8_t button_num();
uint8_t button_gpio(uint8_t id);
uint16_t button_read();
#endif
+416
View File
@@ -0,0 +1,416 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#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 <joy|nkro|both>\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 <first> <second>\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 <touch> [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);
}
+13
View File
@@ -0,0 +1,13 @@
/*
* Mai Controller Command Line
* WHowe <github.com/whowechina>
*/
#ifndef CMD_H
#define CMD_H
void cmd_init();
void cmd_run();
void fps_count(int core);
#endif
+38 -43
View File
@@ -1,68 +1,63 @@
/*
* Controller Config Data
* Controller Config and Runtime Data
* WHowe <github.com/whowechina>
*
* 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);
}
+26 -30
View File
@@ -9,38 +9,34 @@
#include <stdint.h>
#include <stdbool.h>
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
-38
View File
@@ -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"
}
}
}
+150 -156
View File
@@ -3,202 +3,183 @@
* WHowe <github.com/whowechina>
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#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, &reg, 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;
}
}
+200
View File
@@ -0,0 +1,200 @@
/*
* MP121 Captive Touch Sensor
* WHowe <github.com/whowechina>
*
*/
#include <stdint.h>
#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, &reg, 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, &reg, 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);
}
+18
View File
@@ -0,0 +1,18 @@
/*
* MP121 Captive Touch Sensor
* WHowe <github.com/whowechina>
*
*/
#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
+58 -234
View File
@@ -6,7 +6,6 @@
#include "rgb.h"
#include "buttons.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
@@ -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 };
}
+8 -29
View File
@@ -6,44 +6,23 @@
#ifndef RGB_H
#define RGB_H
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#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
+31 -27
View File
@@ -1,8 +1,8 @@
/*
* Controller Save Save and Load
* Controller Config Save and Load
* WHowe <github.com/whowechina>
*
* 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) {
+4 -1
View File
@@ -7,11 +7,14 @@
#define SAVE_H
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#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();
-725
View File
@@ -1,725 +0,0 @@
/*
* Controller Setup Menu
* WHowe <github.com/whowechina>
*
* Setup is a mode, so one can change settings live
*/
#include "setup.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#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()
{
}
-19
View File
@@ -1,19 +0,0 @@
/*
* Controller Setup
* WHowe <github.com/whowechina>
*/
#ifndef SETUP_H
#define SETUP_H
#include <stdint.h>
#include <stdbool.h>
#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
+69
View File
@@ -0,0 +1,69 @@
/*
* Mai Pico Silder Keys
* WHowe <github.com/whowechina>
*
* MPR121 CapSense based Keys
*/
#include "touch.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#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);
}
}
+17
View File
@@ -0,0 +1,17 @@
/*
* Mai Pico Touch Keys
* WHowe <github.com/whowechina>
*/
#ifndef TOUCH_H
#define TOUCH_H
#include <stdint.h>
#include <stdbool.h>
void touch_init();
void touch_update();
bool touch_touched(unsigned key);
void touch_update_config();
#endif
+1 -1
View File
@@ -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
+105 -79
View File
@@ -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;
}
+117 -47
View File
@@ -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_ */
// 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_ */