From 39eb467824db9fafe8837bd3ee00db30999c11b5 Mon Sep 17 00:00:00 2001 From: whowechina Date: Sat, 23 Sep 2023 00:29:32 +0800 Subject: [PATCH] Use MPR121 internal logic now --- Production/Firmware/chu_pico.uf2 | Bin 117248 -> 116224 bytes firmware/CMakeLists.txt | 1 - firmware/lib/pico-mpr121/CMakeLists.txt | 17 - firmware/lib/pico-mpr121/include/mpr121.h | 363 ---------------------- firmware/lib/pico-mpr121/mpr121.c | 170 ---------- firmware/src/CMakeLists.txt | 5 +- firmware/src/board_defs.h | 2 +- firmware/src/cmd.c | 50 +-- firmware/src/config.c | 20 +- firmware/src/main.c | 4 +- firmware/src/mpr121.c | 186 +++++++++++ firmware/src/mpr121.h | 15 + firmware/src/slider.c | 144 +-------- firmware/src/slider.h | 8 +- 14 files changed, 256 insertions(+), 729 deletions(-) delete mode 100644 firmware/lib/pico-mpr121/CMakeLists.txt delete mode 100644 firmware/lib/pico-mpr121/include/mpr121.h delete mode 100644 firmware/lib/pico-mpr121/mpr121.c create mode 100644 firmware/src/mpr121.c create mode 100644 firmware/src/mpr121.h diff --git a/Production/Firmware/chu_pico.uf2 b/Production/Firmware/chu_pico.uf2 index ec2531ec477dde33b111a12dc81d6c9071163aef..8210867ed36eead8521f497e52ab353630192742 100644 GIT binary patch delta 23292 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 delta 24768 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 I_XXwu0r@X{bN~PV diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index ec4d575..b328053 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -9,4 +9,3 @@ set(CMAKE_C_STANDARD 11) pico_sdk_init() add_subdirectory(src) -add_subdirectory(lib/pico-mpr121 mpr121) diff --git a/firmware/lib/pico-mpr121/CMakeLists.txt b/firmware/lib/pico-mpr121/CMakeLists.txt deleted file mode 100644 index bf7542b..0000000 --- a/firmware/lib/pico-mpr121/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -add_library(pico-mpr121 INTERFACE) - -target_include_directories(pico-mpr121 - INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/include -) - -target_link_libraries(pico-mpr121 - INTERFACE - hardware_i2c -) - -target_sources(pico-mpr121 - INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/mpr121.c - ${CMAKE_CURRENT_LIST_DIR}/include/mpr121.h -) diff --git a/firmware/lib/pico-mpr121/include/mpr121.h b/firmware/lib/pico-mpr121/include/mpr121.h deleted file mode 100644 index f5abf5d..0000000 --- a/firmware/lib/pico-mpr121/include/mpr121.h +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright (c) 2021-2022 Antonio González - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef _MPR121_H_ -#define _MPR121_H_ - -#include "pico.h" -#include "hardware/i2c.h" - -/** \file mpr121.h - * \brief Library for using an MPR121-based touch sensor with the - * Raspberry Pi Pico - * -*/ - -typedef struct mpr121_sensor { - i2c_inst_t *i2c_port; - uint8_t i2c_addr; - // uint8_t i2c_sda; - // uint8_t ic2_scl; -} mpr121_sensor_t; - -/*! \brief MPR121 register map - */ -enum mpr121_register { - MPR121_TOUCH_STATUS_REG = 0x00u, - MPR121_OUT_OF_RANGE_STATUS_0_REG = 0x02u, - MPR121_OUT_OF_RANGE_STATUS_1_REG = 0x03u, - MPR121_ELECTRODE_FILTERED_DATA_REG = 0x04u, - MPR121_BASELINE_VALUE_REG = 0x1Eu, - // Registers 0x2B ~ 0x7F are control and configuration registers - MPR121_MAX_HALF_DELTA_RISING_REG = 0x2Bu, - MPR121_NOISE_HALF_DELTA_RISING_REG = 0x2Cu, - MPR121_NOISE_COUNT_LIMIT_RISING_REG = 0x2Du, - MPR121_FILTER_DELAY_COUNT_RISING_REG = 0x2Eu, - MPR121_MAX_HALF_DELTA_FALLING_REG = 0x2Fu, - MPR121_NOISE_HALF_DELTA_FALLING_REG = 0x30u, - MPR121_NOISE_COUNT_LIMIT_FALLING_REG = 0x31u, - MPR121_FILTER_DELAY_COUNT_FALLING_REG = 0x32u, - MPR121_NOISE_HALF_DELTA_TOUCHED_REG = 0x33u, - MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG = 0x34u, - MPR121_FILTER_DELAY_COUNT_TOUCHED_REG = 0x35u, - // (ELEPROX 0x36 .. 0x40) - MPR121_TOUCH_THRESHOLD_REG = 0x41u, - MPR121_RELEASE_THRESHOLD_REG = 0x42u, - // (ELEPROX 0x59 .. 0x5A) - MPR121_DEBOUNCE_REG = 0x5Bu, - MPR121_AFE_CONFIG_REG = 0x5Cu, - MPR121_FILTER_CONFIG_REG = 0x5Du, - MPR121_ELECTRODE_CONFIG_REG = 0x5Eu, - MPR121_ELECTRODE_CURRENT_REG = 0x5Fu, - MPR121_ELECTRODE_CHARGE_TIME_REG = 0x6Cu, - MPR121_GPIO_CTRL_0_REG = 0x73u, - MPR121_GPIO_CTRL_1_REG = 0x74u, - MPR121_GPIO_DATA_REG = 0x75u, - MPR121_GPIO_DIRECTION_REG = 0x76u, - MPR121_GPIO_ENABLE_REG = 0x77u, - MPR121_GPIO_DATA_SET_REG = 0x78u, - MPR121_GPIO_DATA_CLEAR_REG = 0x79u, - MPR121_GPIO_DATA_TOGGLE_REG = 0x7Au, - MPR121_AUTOCONFIG_CONTROL_0_REG = 0x7Bu, - MPR121_AUTOCONFIG_CONTROL_1_REG = 0x7Cu, - MPR121_AUTOCONFIG_USL_REG = 0x7Du, - MPR121_AUTOCONFIG_LSL_REG = 0x7Eu, - MPR121_AUTOCONFIG_TARGET_REG = 0x7Fu, - MPR121_SOFT_RESET_REG = 0x80u -}; - -/*! \brief Initialise the MPR121 and configure registers - * - * The default parameters used here to configure the sensor are as in - * the MPR121 Quick Start Guide (AN3944). - * - * \param i2c_port The I2C instance, either i2c0 or i2c1 - * \param i2c_addr The I2C address of the MPR121 device - * \param sensor Pointer to the structure that stores the MPR121 info - */ -void mpr121_init(i2c_inst_t *i2c_port, uint8_t i2c_addr, - mpr121_sensor_t *sensor); - -/*! \brief Write a value to the specified register - * - * \param reg The register address - * \param val The value to write - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_write(enum mpr121_register reg, uint8_t val, - mpr121_sensor_t *sensor) { - uint8_t buf[] = {reg, val}; - i2c_write_blocking(sensor->i2c_port, sensor->i2c_addr, buf, 2, - false); -} - -/*! \brief Read a byte from the specified register - * - * \param reg The register address - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_read(enum mpr121_register reg, uint8_t *dst, - mpr121_sensor_t *sensor) { - i2c_write_blocking(sensor->i2c_port, sensor->i2c_addr, ®, 1, - true); - i2c_read_blocking(sensor->i2c_port, sensor->i2c_addr, dst, 1, - false); -} - -/*! \brief Read a 2-byte value from the specified register - * - * \param reg The register address - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_read16(enum mpr121_register reg, uint16_t *dst, - mpr121_sensor_t *sensor) { - uint8_t vals[2]; - i2c_write_blocking(sensor->i2c_port, sensor->i2c_addr, ®, 1, - true); - i2c_read_blocking(sensor->i2c_port, sensor->i2c_addr, vals, 2, - false); - *dst = vals[1] << 8 | vals[0]; -} - -/*! \brief Set touch and release thresholds - * - * From the MPR121 datasheet (section 5.6): - * > In a typical application, touch threshold is in the range 4--16, - * > and it is several counts larger than the release threshold. This - * > is to provide hysteresis and to prevent noise and jitter. - * - * \param touch Touch threshold in the range 0--255 - * \param release Release threshold in the range 0--255 - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_set_thresholds(uint8_t touch, uint8_t release, - mpr121_sensor_t *sensor) { - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - if (config != 0){ - // Stop mode - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - } - - for (uint8_t i=0; i<12; i++) { - mpr121_write(MPR121_TOUCH_THRESHOLD_REG + i * 2, touch, sensor); - mpr121_write(MPR121_RELEASE_THRESHOLD_REG + i * 2, release, - sensor); - } - - if (config != 0){ - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); - } -} - -/*! \brief Enable only the number of electrodes specified - * - * \param nelec Number of electrodes to enable - * \param sensor Pointer to the structure that stores the MPR121 info - * - * E.g. if `nelec` is 3, electrodes 0 to 2 will be enabled; if `nelec` - * is 6, electrodes 0 to 5 will be enabled. From the datasheet: - * "Enabling specific channels will save the scan time and sensing - * field power spent on the unused channels." - */ -static void mpr121_enable_electrodes(uint8_t nelec, - mpr121_sensor_t *sensor){ - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - - // Clear bits 3-0, which controls the operation of the 12 - // electrodes. - config &= ~0x0f; - - // Set number of electrodes enabled - config |= nelec; - - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); -} - -/*! \brief Read the touch/release status of all 13 input channels - * - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - * - * In the value read, bits 11-0 represent electrodes 11 to 0, - * respectively, and bit 12 is the proximity detection channel. Each - * bit represent the status of these channels: 1 if the channel is - * touched, 0 if it is released. - */ -static void mpr121_touched(uint16_t *dst, mpr121_sensor_t *sensor) { - mpr121_read16(MPR121_TOUCH_STATUS_REG, dst, sensor); - *dst &= 0x0fff; -} - -/*! \brief Determine whether an electrode has been touched - * - * \param electrode Electrode number - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_is_touched(uint8_t electrode, bool *dst, - mpr121_sensor_t *sensor){ - uint16_t touched; - mpr121_touched(&touched, sensor); - *dst = (bool) ((touched >> electrode) & 1); -} - -/*! \brief Read an electrode's filtered data value - * - * \param electrode Electrode number - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - * - * The data range of the filtered data is 0 to 1024. - * \sa mpr121_baseline_value - */ -static void mpr121_filtered_data(uint8_t electrode, uint16_t *dst, - mpr121_sensor_t *sensor){ - mpr121_read16(MPR121_ELECTRODE_FILTERED_DATA_REG + (electrode * 2), - dst, sensor); - // Filtered data is 10-bit - *dst &= 0x3ff; -} - -/*! \brief Read an electrode's baseline value - * - * \param electrode Electrode number - * \param dst Pointer to buffer to receive data - * \param sensor Pointer to the structure that stores the MPR121 info - * - * From the MPR112 datasheet: - * > Along with the 10-bit electrode filtered data output, each channel - * > also has a 10-bit baseline value. These values are the output of - * > the internal baseline filter operation tracking the slow-voltage - * > variation of the background capacitance change. Touch/release - * > detection is made based on the comparison between the 10-bit - * > electrode filtered data and the 10-bit baseline value. - * - * > Although internally the baseline value is 10-bit, users can only - * > access the 8 MSB of the 10-bit baseline value through the baseline - * > value registers. - * - * \sa mpr121_filtered_data - */ -static void mpr121_baseline_value(uint8_t electrode, uint16_t *dst, - mpr121_sensor_t *sensor){ - uint8_t baseline; - mpr121_read(MPR121_BASELINE_VALUE_REG + electrode, &baseline, - sensor); - // From the datasheet: Although internally the baseline value is - // 10-bit, users can only access the 8 MSB of the 10-bit baseline - // value through the baseline value registers. The read out from the - // baseline register must be left shift two bits before comparing it - // with the 10-bit electrode data. - *dst = baseline << 2; -} - -/*! \brief Set the Max Half Delta - * - * The Max Half Delta determines the largest magnitude of variation to - * pass through the third level filter. See application note MPR121 - * Baseline System (AN3891) for details. - * - * \param rising Value in the range 1~63 - * \param falling Value in the range 1~63 - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_set_max_half_delta(uint8_t rising, uint8_t falling, - mpr121_sensor_t *sensor) { - // Read current configuration then enter stop mode - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - // Write MHD values - mpr121_write(MPR121_MAX_HALF_DELTA_RISING_REG, rising, sensor); - mpr121_write(MPR121_MAX_HALF_DELTA_FALLING_REG, falling, sensor); - // Re-enable electrodes - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); -} - -/*! \brief Set the Noise Half Delta - * - * The Noise Half Delta determines the incremental change when - * non-noise drift is detected. See application note MPR121 Baseline - * System (AN3891) for details. - * - * \param rising Value in the range 1~63 - * \param falling Value in the range 1~63 - * \param touched Value in the range 1~63 - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_set_noise_half_delta(uint8_t rising, uint8_t falling, - uint8_t touched, mpr121_sensor_t *sensor) { - // Read current configuration then enter stop mode - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - // Write NHD values - mpr121_write(MPR121_NOISE_HALF_DELTA_RISING_REG, rising, sensor); - mpr121_write(MPR121_NOISE_HALF_DELTA_FALLING_REG, falling, sensor); - mpr121_write(MPR121_NOISE_HALF_DELTA_TOUCHED_REG, touched, sensor); - // Re-enable electrodes - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); -} - -/*! \brief Set the Noise Count Limit - * - * The Noise Count Limit determines the number of samples consecutively - * greater than the Max Half Delta necessary before it can be - * determined that it is non-noise. See application note MPR121 Baseline - * System (AN3891) for details. - * - * \param rising Value in the range 0~255 - * \param falling Value in the range 0~255 - * \param touched Value in the range 0~255 - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_set_noise_count_limit(uint8_t rising, - uint8_t falling, uint8_t touched, mpr121_sensor_t *sensor) { - // Read current configuration then enter stop mode - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - // Write new NCL values - mpr121_write(MPR121_NOISE_COUNT_LIMIT_RISING_REG, rising, sensor); - mpr121_write(MPR121_NOISE_COUNT_LIMIT_FALLING_REG, falling, sensor); - mpr121_write(MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG, touched, sensor); - // Re-enable electrodes - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); -} - -/*! \brief Set the Filter Delay Limit - * - * The Filter Delay Limit determines the rate of operation of the - * filter. A larger number makes it operate slower. See application - * note MPR121 Baseline System (AN3891) for details. - * - * \param rising Value in the range 0~255 - * \param falling Value in the range 0~255 - * \param touched Value in the range 0~255 - * \param sensor Pointer to the structure that stores the MPR121 info - */ -static void mpr121_set_filter_delay_limit(uint8_t rising, - uint8_t falling, uint8_t touched, mpr121_sensor_t *sensor) { - // Read current configuration then enter stop mode - uint8_t config; - mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - // Write new FDL values - mpr121_write(MPR121_FILTER_DELAY_COUNT_RISING_REG, rising, sensor); - mpr121_write(MPR121_FILTER_DELAY_COUNT_FALLING_REG, falling, - sensor); - mpr121_write(MPR121_FILTER_DELAY_COUNT_TOUCHED_REG, touched, - sensor); - // Re-enable electrodes - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); -} - -#endif diff --git a/firmware/lib/pico-mpr121/mpr121.c b/firmware/lib/pico-mpr121/mpr121.c deleted file mode 100644 index 7586675..0000000 --- a/firmware/lib/pico-mpr121/mpr121.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2021-2022 Antonio González - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "mpr121.h" - -void mpr121_init(i2c_inst_t *i2c_port, uint8_t i2c_addr, - mpr121_sensor_t *sensor) { - sensor->i2c_port = i2c_port; - sensor->i2c_addr = i2c_addr; - - // Enter stop mode by setting ELEPROX_EN and ELE_EN bits to zero. - // This is needed because register write operations can only take - // place in stop mode. - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); - - // Writing 0x80 (SOFT_RESET) with 0x63 asserts soft reset. - mpr121_write(MPR121_SOFT_RESET_REG, 0x63, sensor); - - // == Capacitance sensing settings (AN2889), Filtering and ========= - // timing settings (AN3890) - - // These settings are configured in two registers: the Filter and - // Global CDC CDT Configuration registers (0x5C, 0x5D). - // - // Charge-discharge current (CDC) and charge-discharge time (CDT) - // can be configured globally or on a per-electrode basis. Here, - // The global CDC and CDT values are set to their defaults, and then - // these values are overriden by independently configuring each - // electrode (auto-configuration). - - // Filter/global CDC configuration register (0x5C) - // - // First filter iterations (FFI), bits 7-6. Number of samples taken - // as input to the first level of filtering. Default is 0b00 (sets - // samples taken to 6) - // - // Charge-discharge current (CDC), bits 5-0. Sets the value of - // charge-discharge current applied to the electrode. Max is 63 µA - // in 1 µA steps. Default is 0b010000 (16 µA) - // - // AFE configuration register default, 0b00010000 = 0x10 - mpr121_write(MPR121_AFE_CONFIG_REG, 0x10, sensor); - - // Filter/global CDC configuration register (0x5D) - // - // Charge discharge time (CDT), bits 7-5. Selects the global value - // of charge time applied to electrode. The maximum is 32 μs, - // programmable as 2^(n-2) μs. Default is 0b001 (time is set to - // 0.5 µs) - // - // Second filter iterations (SFI), bits 4-3. Selects the number of - // samples taken for the second level filter. Default is 0b00 - // (number of samples is set to 4) - // - // Electrode sample interval (ESI), bits 2-0. Controls the sampling - // rate of the device. The maximum is 128 ms, programmable to 2^n - // ms. Decrease this value for better response time, increase to - // save power. Default is 0b100 (period set to 16 ms). - // - // Filter configuration register default, 0b00100100 = 0x24 - // I do not need power saving features but I want fast responses, - // so I set this to 0x20. - mpr121_write(MPR121_FILTER_CONFIG_REG, 0x20, sensor); - - // Auto-configuration - // - // Sets automatically charge current (CDC) and time (CDT) values for - // each electrode. - // - // Autoconfig USL register: the upper limit for the - // auto-configuration. This value (and those that follow below) - // were calculated based on Vdd = 3.3 V and following the equations - // in NXP Application Note AN3889. - // USL = 201 = 0xC9 - mpr121_write(MPR121_AUTOCONFIG_USL_REG, 0xC9, sensor); - - // Autoconfig target level register: the target level for the - // auto-configuration baseline search. - // TL = 181 = 0xB5 - mpr121_write(MPR121_AUTOCONFIG_TARGET_REG, 0xB5, sensor); - - // Autoconfig LSL register: the lower limit for the - // auto-configuration. - // LSL = 131 = 0x83 - mpr121_write(MPR121_AUTOCONFIG_LSL_REG, 0x83, sensor); - - // Autoconfiguration control register. Default value is 0b00001011 = - // 0x0B, where: - // - // First filter iterations (FFI), bits 7-6. Must be the same value - // of FFI as in register MPR121_AFE_CONFIG_REG (0x5C) above; - // default is 0b00. - // - // Retry, bits 5-4. Default is disabled, 0b00. - // - // Baseline value adjust (BVA), bits 3-2. This value must be the - // same as the CL (calibration lock) value in the Electrode - // Configuration Register, below, i.e. 0b10. - // - // Automatic Reconfiguration Enable (ARE), bit 1. Default is 0b1, - // enabled. - // - // Automatic Reconfiguration Enable (ACE), bit 0. Default is 0b1, - // enabled. - mpr121_write(MPR121_AUTOCONFIG_CONTROL_0_REG, 0x0B, sensor); - - // == Baseline system (AN3891) ===================================== - - // Maximum Half Delta (MHD): Determines the largest magnitude of - // variation to pass through the baseline filter. The range of the - // effective value is 1~63. - mpr121_write(MPR121_MAX_HALF_DELTA_RISING_REG, 0x01, sensor); - mpr121_write(MPR121_MAX_HALF_DELTA_FALLING_REG, 0x01, sensor); - - // Noise Half Delta (NHD): Determines the incremental change when - // non-noise drift is detected. The range of the effective value is - // 1~63. - mpr121_write(MPR121_NOISE_HALF_DELTA_RISING_REG, 0x01, sensor); - mpr121_write(MPR121_NOISE_HALF_DELTA_FALLING_REG, 0x01, sensor); - mpr121_write(MPR121_NOISE_HALF_DELTA_TOUCHED_REG, 0x01, sensor); - - // Noise Count Limit (NCL): Determines the number of samples - // consecutively greater than the Max Half Delta value. This is - // necessary to determine that it is not noise. The range of the - // effective value is 0~255. - mpr121_write(MPR121_NOISE_COUNT_LIMIT_RISING_REG, 0x00, sensor); - mpr121_write(MPR121_NOISE_COUNT_LIMIT_FALLING_REG, 0xFF, sensor); - mpr121_write(MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG, 0x00, sensor); - - // Filter Delay Count Limit (FDL): Determines the operation rate of - // the filter. A larger count limit means the filter delay is - // operating more slowly. The range of the effective value is 0~255. - mpr121_write(MPR121_FILTER_DELAY_COUNT_RISING_REG, 0x00, sensor); - mpr121_write(MPR121_FILTER_DELAY_COUNT_FALLING_REG, 0x02, sensor); - mpr121_write(MPR121_FILTER_DELAY_COUNT_TOUCHED_REG, 0x00, sensor); - - // == Debounce and thresholds (AN3892) ============================= - - // Debounce. Value range for each is 0~7. - // Bits 2-0, debounce touch (DT). - // Bits 6-4, debounce release (DR). - mpr121_write(MPR121_DEBOUNCE_REG, 0x00, sensor); - - // Touch and release threshold values for all electrodes. - for (uint8_t i=0; i<12; i++) { - mpr121_write(MPR121_TOUCH_THRESHOLD_REG + i * 2, 0x0F, sensor); - mpr121_write(MPR121_RELEASE_THRESHOLD_REG + i * 2, 0x0A, sensor); - } - - // Electrode Configuration Register (ECR, 0x5E). This must be the - // last register to write to because setting ELEPROX_EN and/or - // ELE_EN to non-zero puts the sensor in Run Mode. - // - // Calibration lock (CL), bits 7-6. The default on reset is 0b00 - // (CL enabled). Here I set this instead to 0b10 because this - // enables baseline tracking with initial baseline value loaded - // with the 5 high bits of the first electrode data value, which - // makes the sensor stabilise sooner. Note that ths value must - // match BVA bits in the Auto-configure Control Register above. - // - // Proximity enable (ELEPROX_EN), bits 5-4. Default, 0b00 - // (proximity detection disabled). - // - // Electrode enabled (ELE_EN), bits 3-0. Default, 0b1100 (enable - // all 12 electrodes). - mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x8C, sensor); -} \ No newline at end of file diff --git a/firmware/src/CMakeLists.txt b/firmware/src/CMakeLists.txt index df1c713..3917ecd 100644 --- a/firmware/src/CMakeLists.txt +++ b/firmware/src/CMakeLists.txt @@ -4,7 +4,8 @@ set(LWIP_ROOT ${PICO_SDK_PATH}/lib/lwip) function(make_firmware board board_def) pico_sdk_init() add_executable(${board} - main.c slider.c air.c rgb.c save.c config.c cmd.c lzfx.c vl53l0x.c usb_descriptors.c) + main.c slider.c air.c rgb.c save.c config.c cmd.c lzfx.c + vl53l0x.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) @@ -20,7 +21,7 @@ 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}) diff --git a/firmware/src/board_defs.h b/firmware/src/board_defs.h index 6734aac..ad95788 100644 --- a/firmware/src/board_defs.h +++ b/firmware/src/board_defs.h @@ -8,7 +8,7 @@ #define I2C_PORT i2c0 #define I2C_SDA 16 #define I2C_SCL 17 -#define I2C_FREQ 733*1000 +#define I2C_FREQ 533*1000 #define I2C_HUB_EN 19 diff --git a/firmware/src/cmd.c b/firmware/src/cmd.c index 1bc0b5b..4bcdc14 100644 --- a/firmware/src/cmd.c +++ b/firmware/src/cmd.c @@ -11,8 +11,8 @@ #include "slider.h" #include "save.h" -#define SENSE_LIMIT_MAX 8 -#define SENSE_LIMIT_MIN -8 +#define SENSE_LIMIT_MAX 7 +#define SENSE_LIMIT_MIN -7 #define MAX_COMMANDS 20 #define MAX_COMMAND_LENGTH 20 @@ -76,15 +76,15 @@ static void list_sense() printf("[Sense]\n"); printf(" Global: %d, debounce (touch, release): %d, %d\n", chu_cfg->sense.global, chu_cfg->sense.debounce_touch, chu_cfg->sense.debounce_release); - printf(" | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16|\n"); - printf(" -----------------------------------------------------------------\n"); + 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("%3d|", chu_cfg->sense.keys[i * 2]); + printf("%2d|", chu_cfg->sense.keys[i * 2]); } printf("\n B |"); for (int i = 0; i < 16; i++) { - printf("%3d|", chu_cfg->sense.keys[i * 2 + 1]); + printf("%2d|", chu_cfg->sense.keys[i * 2 + 1]); } printf("\n"); } @@ -244,12 +244,12 @@ static uint8_t *extract_key(const char *param) static void handle_sense(int argc, char *argv[]) { - const char *usage = "Usage: sense [key] <+|->\n" + const char *usage = "Usage: sense [key] <+|-|0>\n" "Example:\n" " >sense +\n" " >sense -\n" " >sense 1A +\n" - " >sense 13B -\n"; + " >sense 13B 0\n"; if ((argc < 1) || (argc > 2)) { printf(usage); return; @@ -273,11 +273,14 @@ static void handle_sense(int argc, char *argv[]) if (*target > SENSE_LIMIT_MIN) { (*target)--; } + } else if (strcmp(op, "0") == 0) { + *target = 0; } else { printf(usage); return; } + slider_update_config(); config_changed(); list_sense(); } @@ -285,7 +288,7 @@ static void handle_sense(int argc, char *argv[]) static void handle_debounce(int argc, char *argv[]) { const char *usage = "Usage: debounce [release]\n" - " touch, release: 0-255\n"; + " touch, release: 0-7\n"; if ((argc < 1) || (argc > 2)) { printf(usage); return; @@ -300,7 +303,8 @@ static void handle_debounce(int argc, char *argv[]) release = extract_non_neg_int(argv[1], 0); } - if ((touch < 0) || (release < 0)) { + if ((touch < 0) || (release < 0) || + (touch > 7) || (release > 7)) { printf(usage); return; } @@ -308,34 +312,11 @@ static void handle_debounce(int argc, char *argv[]) chu_cfg->sense.debounce_touch = touch; chu_cfg->sense.debounce_release = release; + slider_update_config(); config_changed(); list_sense(); } -static void handle_baseline() -{ - 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("%3d|", slider_baseline(i * 2)); - } - printf("\n B |"); - for (int i = 0; i < 16; i++) { - printf("%3d|", slider_baseline(i * 2 + 1)); - } - printf("\n"); - printf(" dA |"); - for (int i = 0; i < 16; i++) { - printf("%3d|", slider_delta(i * 2)); - } - printf("\n dB |"); - for (int i = 0; i < 16; i++) { - printf("%3d|", slider_delta(i * 2 + 1)); - } - printf("\n"); -} - static void handle_save() { save_request(true); @@ -356,7 +337,6 @@ void cmd_init() register_command("tof", handle_tof); register_command("sense", handle_sense); register_command("debounce", handle_debounce); - register_command("baseline", handle_baseline); register_command("save", handle_save); register_command("factory", config_factory_reset); } diff --git a/firmware/src/config.c b/firmware/src/config.c index 00de528..9e8c253 100644 --- a/firmware/src/config.c +++ b/firmware/src/config.c @@ -30,8 +30,8 @@ static chu_cfg_t default_cfg = { .pitch = 18, }, .sense = { - .debounce_touch = 1, - .debounce_release = 8, + .debounce_touch = 0, + .debounce_release = 1, }, .hid = { .joy = 0x0f, @@ -52,6 +52,22 @@ static void config_loaded() chu_cfg->tof = default_cfg.tof; config_changed(); } + if ((chu_cfg->sense.global > 7) || (chu_cfg->sense.global < -7)) { + chu_cfg->sense.global = default_cfg.sense.global; + config_changed(); + } + for (int i = 0; i < 32; i++) { + if ((chu_cfg->sense.keys[i] > 7) || (chu_cfg->sense.keys[i] < -7)) { + chu_cfg->sense.keys[i] = default_cfg.sense.keys[i]; + config_changed(); + } + } + if ((chu_cfg->sense.debounce_touch > 7) | + (chu_cfg->sense.debounce_release > 7)) { + chu_cfg->sense.debounce_touch = default_cfg.sense.debounce_touch; + chu_cfg->sense.debounce_release = default_cfg.sense.debounce_release; + config_changed(); + } } void config_changed() diff --git a/firmware/src/main.c b/firmware/src/main.c index 62d015c..9c0a536 100644 --- a/firmware/src/main.c +++ b/firmware/src/main.c @@ -138,11 +138,9 @@ static void core1_loop() { while (1) { if (mutex_try_enter(&core1_io_lock, NULL)) { - run_lights(); rgb_update(); mutex_exit(&core1_io_lock); } - slider_update_baseline(); fps_count(1); sleep_ms(1); } @@ -162,6 +160,8 @@ static void core0_loop() gen_nkro_report(); report_usb_hid(); tud_task(); + + run_lights(); } } diff --git a/firmware/src/mpr121.c b/firmware/src/mpr121.c new file mode 100644 index 0000000..440bc94 --- /dev/null +++ b/firmware/src/mpr121.c @@ -0,0 +1,186 @@ +/* + * MP121 Captive Touch Sensor + * WHowe + * + */ + +#include +#include "hardware/i2c.h" + +#include "mpr121.h" +#include "board_defs.h" + +#define IO_TIMEOUT_US 1000 + +#define TOUCH_THRESHOLD_BASE 17 +#define RELEASE_THRESHOLD_BASE 10 + +#define MPR121_TOUCH_STATUS_REG 0x00 +#define MPR121_OUT_OF_RANGE_STATUS_0_REG 0x02 +#define MPR121_OUT_OF_RANGE_STATUS_1_REG 0x03 +#define MPR121_ELECTRODE_FILTERED_DATA_REG 0x04 +#define MPR121_BASELINE_VALUE_REG 0x1E + +#define MPR121_MAX_HALF_DELTA_RISING_REG 0x2B +#define MPR121_NOISE_HALF_DELTA_RISING_REG 0x2C +#define MPR121_NOISE_COUNT_LIMIT_RISING_REG 0x2D +#define MPR121_FILTER_DELAY_COUNT_RISING_REG 0x2E +#define MPR121_MAX_HALF_DELTA_FALLING_REG 0x2F +#define MPR121_NOISE_HALF_DELTA_FALLING_REG 0x30 +#define MPR121_NOISE_COUNT_LIMIT_FALLING_REG 0x31 +#define MPR121_FILTER_DELAY_COUNT_FALLING_REG 0x32 +#define MPR121_NOISE_HALF_DELTA_TOUCHED_REG 0x33 +#define MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG 0x34 +#define MPR121_FILTER_DELAY_COUNT_TOUCHED_REG 0x35 + +#define MPR121_TOUCH_THRESHOLD_REG 0x41 +#define MPR121_RELEASE_THRESHOLD_REG 0x42 + +#define MPR121_DEBOUNCE_REG 0x5B +#define MPR121_AFE_CONFIG_REG 0x5C +#define MPR121_FILTER_CONFIG_REG 0x5D +#define MPR121_ELECTRODE_CONFIG_REG 0x5E +#define MPR121_ELECTRODE_CURRENT_REG 0x5F +#define MPR121_ELECTRODE_CHARGE_TIME_REG 0x6C +#define MPR121_GPIO_CTRL_0_REG 0x73 +#define MPR121_GPIO_CTRL_1_REG 0x74 +#define MPR121_GPIO_DATA_REG 0x75 +#define MPR121_GPIO_DIRECTION_REG 0x76 +#define MPR121_GPIO_ENABLE_REG 0x77 +#define MPR121_GPIO_DATA_SET_REG 0x78 +#define MPR121_GPIO_DATA_CLEAR_REG 0x79 +#define MPR121_GPIO_DATA_TOGGLE_REG 0x7A +#define MPR121_AUTOCONFIG_CONTROL_0_REG 0x7B +#define MPR121_AUTOCONFIG_CONTROL_1_REG 0x7C +#define MPR121_AUTOCONFIG_USL_REG 0x7D +#define MPR121_AUTOCONFIG_LSL_REG 0x7E +#define MPR121_AUTOCONFIG_TARGET_REG 0x7F +#define MPR121_SOFT_RESET_REG 0x80 + +static void write_reg(uint8_t addr, uint8_t reg, uint8_t val) +{ + uint8_t buf[] = {reg, val}; + i2c_write_blocking_until(I2C_PORT, addr, buf, 2, false, + time_us_64() + IO_TIMEOUT_US); +} + +static uint8_t read_reg(uint8_t addr, uint8_t reg) +{ + uint8_t value; + i2c_write_blocking_until(I2C_PORT, addr, ®, 1, true, + time_us_64() + IO_TIMEOUT_US); + i2c_read_blocking_until(I2C_PORT, addr, &value, 1, false, + time_us_64() + IO_TIMEOUT_US); + return value; +} + +void mpr121_init(uint8_t i2c_addr) +{ + write_reg(i2c_addr, 0x80, 0x63); // Soft reset MPR121 if not reset correctly + + //touch pad baseline filter + //rising: baseline quick rising + write_reg(i2c_addr, 0x2B, 0x01); // Max half delta Rising + write_reg(i2c_addr, 0x2C, 0x01); // Noise half delta Rising + write_reg(i2c_addr, 0x2D, 0x00); // Noise count limit Rising + write_reg(i2c_addr, 0x2E, 0x00); // Delay limit Rising + + //falling: baseline slow falling + write_reg(i2c_addr, 0x2F, 0x01); // Max half delta Falling + write_reg(i2c_addr, 0x30, 0x01); // Noise half delta Falling + write_reg(i2c_addr, 0x31, 0xFF); // Noise count limit Falling + write_reg(i2c_addr, 0x32, 0x0); // 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 (uint8_t 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, 0b01010000); // 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, 0b01001001); // AFES=6 samples, same as AFES in 0x5C + // retry=2b00, no retry, + // BVA=2b10, load 5MSB after AC, + // ARE/ACE=2b11, auto configuration enabled + //write_reg(i2c_addr, 0x7C,0x80); // Skip charge time search, use setting in 0x5D, + // OOR, AR, AC IE disabled + // Not used. Possible Proximity CDC shall over 63uA + // if only use 0.5uS CDT, the TGL for proximity cannot meet + // Possible if manually set Register0x72=0x03 + // (Auto configure result) alone. + write_reg(i2c_addr, 0x7D, 0xc8); // AC up limit /C8/BD/C0/9C + write_reg(i2c_addr, 0x7E, 0x82); // AC low limit /82/7A/7C/65 + write_reg(i2c_addr, 0x7F, 0xb4); // AC target /B4/AA/AC/8C target for /3.0V/2.8V/1.8V + write_reg(i2c_addr, 0x5E, 0x8C); // Run 12 touch, CL=2b10, load 5MSB to baseline +} + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +static void mpr121_read_many(uint8_t addr, uint8_t reg, uint8_t *buf, size_t n) +{ + i2c_write_blocking_until(I2C_PORT, addr, ®, 1, true, + time_us_64() + IO_TIMEOUT_US); + i2c_read_blocking_until(I2C_PORT, addr, buf, n, false, + time_us_64() + IO_TIMEOUT_US * n / 2); +} + +static void mpr121_read_many16(uint8_t addr, uint8_t reg, uint16_t *buf, size_t n) +{ + uint8_t vals[n * 2]; + mpr121_read_many(addr, reg, vals, n * 2); + for (int i = 0; i < n; i++) { + buf[i] = (vals[i * 2 + 1] << 8) | vals[i * 2]; + } +} + +uint16_t mpr121_touched(uint8_t addr) +{ + uint16_t touched; + mpr121_read_many16(addr, MPR121_TOUCH_STATUS_REG, &touched, 2); + return touched; +} + +static uint8_t mpr121_stop(uint8_t addr) +{ + uint8_t ecr = read_reg(addr, MPR121_ELECTRODE_CONFIG_REG); + write_reg(addr, MPR121_ELECTRODE_CONFIG_REG, ecr & 0xC0); + return ecr; +} + +static uint8_t mpr121_resume(uint8_t addr, uint8_t ecr) +{ + write_reg(addr, MPR121_ELECTRODE_CONFIG_REG, ecr); +} + +void mpr121_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); +} + +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); +} diff --git a/firmware/src/mpr121.h b/firmware/src/mpr121.h new file mode 100644 index 0000000..9972615 --- /dev/null +++ b/firmware/src/mpr121.h @@ -0,0 +1,15 @@ +/* + * MP121 Captive Touch Sensor + * WHowe + * + */ + +#ifndef MP121_H +#define MP121_H + +void mpr121_init(uint8_t addr); +uint16_t mpr121_touched(uint8_t addr); +void mpr121_debounce(uint8_t addr, uint8_t touch, uint8_t release); +void mpr121_sense(uint8_t addr, int8_t sense, int8_t *sense_keys); + +#endif diff --git a/firmware/src/slider.c b/firmware/src/slider.c index 9969a7d..ce09d04 100644 --- a/firmware/src/slider.c +++ b/firmware/src/slider.c @@ -13,14 +13,12 @@ #include "bsp/board.h" #include "hardware/gpio.h" +#include "hardware/i2c.h" #include "board_defs.h" -#include "mpr121.h" #include "config.h" - -#define TOUCH_THRESHOLD 17 -#define RELEASE_THRESHOLD 8 +#include "mpr121.h" #define MPR121_ADDR 0x5A @@ -30,37 +28,6 @@ static uint16_t readout[36]; static bool touched[36]; static uint16_t touch[3]; -static struct mpr121_sensor mpr121[3]; - -#define ABS(x) ((x) < 0 ? -(x) : (x)) - -static void mpr121_read_many(uint8_t addr, uint8_t reg, uint8_t *buf, size_t n) -{ - i2c_write_blocking_until(I2C_PORT, addr, ®, 1, true, time_us_64() + 2000); - i2c_read_blocking_until(I2C_PORT, addr, buf, n, false, time_us_64() + 2000); -} - -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]; - } -} - -static void init_baseline() -{ - sleep_ms(100); - for (int m = 0; m < 3; m++) { - uint8_t vals[12]; - mpr121_read_many(MPR121_ADDR + m, MPR121_BASELINE_VALUE_REG, vals, 12); - for (int i = 0; i < 12; i++) { - baseline[m * 12 + i] = vals[i] * 4; - } - } -} - void slider_init() { i2c_init(I2C_PORT, I2C_FREQ); @@ -70,114 +37,31 @@ void slider_init() gpio_pull_up(I2C_SCL); for (int m = 0; m < 3; m++) { - mpr121_init(I2C_PORT, MPR121_ADDR + m, mpr121 + m); + mpr121_init(MPR121_ADDR + m); } - - init_baseline(); + slider_update_config(); } void slider_update() { - uint8_t reg = MPR121_ELECTRODE_FILTERED_DATA_REG; - mpr121_read_many16(MPR121_ADDR, reg, readout, 12); - mpr121_read_many16(MPR121_ADDR + 1, reg, readout + 12, 12); - mpr121_read_many16(MPR121_ADDR + 2, reg, readout + 24, 12); -// mpr121_touched(touch, mpr121); -// mpr121_touched(touch + 1, mpr121 + 1); -// mpr121_touched(touch + 2, mpr121 + 2); + touch[0] = mpr121_touched(MPR121_ADDR); + touch[1] = mpr121_touched(MPR121_ADDR + 1); + touch[2] = mpr121_touched(MPR121_ADDR + 2); } -void slider_update_baseline() -{ - static int iteration = 0; - - for (int i = 0; i < 32; i++) { - int16_t delta = readout[i] - baseline[i]; - if (ABS(delta) > RELEASE_THRESHOLD) { - continue; - } - error[i] += delta; - } - - iteration++; - if (iteration > 50) { - iteration = 0; - for (int i = 0; i < 32; i++) { - if (error[i] > 50) { - baseline[i] ++; - } else if (error[i] < -50) { - baseline[i] --; - } else { - } - error[i] = 0; - } - } -} - -int slider_value(unsigned key) -{ - if (key >= 32) { - return 0; - } - return readout[key]; -} - -int slider_baseline(unsigned key) -{ - if (key >= 32) { - return 0; - } - return baseline[key]; -} - -int slider_delta(unsigned key) -{ - if (key >= 32) { - return 0; - } - return readout[key] - baseline[key]; -} - -static uint16_t touch_count[36]; -static uint16_t release_count[36]; - bool slider_touched(unsigned key) { if (key >= 32) { return 0; } - int delta = baseline[key] - readout[key]; - - int bias = chu_cfg->sense.global + chu_cfg->sense.keys[key]; - int touch_thre = TOUCH_THRESHOLD - bias; - int release_thre = RELEASE_THRESHOLD - bias / 2; - - if (touched[key]) { - if (delta > release_thre) { - release_count[key] = 0; - } else { - release_count[key]++; - } - if (release_count[key] > chu_cfg->sense.debounce_release) { - touch_count[key] = 0; - touched[key] = false; - } - } else if (!touched[key]) { - if (delta < touch_thre) { - touch_count[key] = 0; - } else { - touch_count[key]++; - } - if (touch_count[key] > chu_cfg->sense.debounce_touch) { - release_count[key] = 0; - touched[key] = true; - } - } - - return touched[key]; + return touch[key / 12] & (1 << (key % 12)); } -uint16_t slider_hw_touch(unsigned m) +void slider_update_config() { - return touch[m]; + for (int m = 0; m < 3; m++) { + mpr121_debounce(MPR121_ADDR + m, chu_cfg->sense.debounce_touch, + chu_cfg->sense.debounce_release); + mpr121_sense(MPR121_ADDR + m, chu_cfg->sense.global, chu_cfg->sense.keys + m * 12); + } } diff --git a/firmware/src/slider.h b/firmware/src/slider.h index aa71041..7462d6a 100644 --- a/firmware/src/slider.h +++ b/firmware/src/slider.h @@ -10,12 +10,8 @@ #include void slider_init(); -int slider_value(unsigned key); -int slider_baseline(unsigned key); -int slider_delta(unsigned key); -bool slider_touched(unsigned key); -uint16_t slider_hw_touch(unsigned m); void slider_update(); -void slider_update_baseline(); +bool slider_touched(unsigned key); +void slider_update_config(); #endif