mirror of
https://github.com/whowechina/chu_pico.git
synced 2025-02-15 01:52:42 +01:00
364 lines
13 KiB
C
364 lines
13 KiB
C
/*
|
|
* 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
|