1
0
mirror of https://github.com/whowechina/chu_pico.git synced 2025-02-17 18:49:18 +01:00

Use MPR121 internal logic now

This commit is contained in:
whowechina 2023-09-23 00:29:32 +08:00
parent 7b1c41d3ca
commit 39eb467824
14 changed files with 256 additions and 729 deletions

Binary file not shown.

View File

@ -9,4 +9,3 @@ set(CMAKE_C_STANDARD 11)
pico_sdk_init()
add_subdirectory(src)
add_subdirectory(lib/pico-mpr121 mpr121)

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
)

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

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);
}

View File

@ -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})

View File

@ -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

View File

@ -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 <touch> [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);
}

View File

@ -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()

View File

@ -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();
}
}

186
firmware/src/mpr121.c Normal file
View File

@ -0,0 +1,186 @@
/*
* 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 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, &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, 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, &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_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);
}

15
firmware/src/mpr121.h Normal file
View File

@ -0,0 +1,15 @@
/*
* 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_debounce(uint8_t addr, uint8_t touch, uint8_t release);
void mpr121_sense(uint8_t addr, int8_t sense, int8_t *sense_keys);
#endif

View File

@ -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, &reg, 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);
}
}

View File

@ -10,12 +10,8 @@
#include <stdbool.h>
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