zenxfi3: rewrite mpr121 driver

The new driver uses an asynchronous architecture for touch status reading.

Change-Id: Ic75a8b91bc47ee16c3af873afde178cd70186376
This commit is contained in:
Amaury Pouly 2016-05-30 16:24:38 +01:00
parent b2afd931e2
commit d42a4a4eb4
7 changed files with 600 additions and 573 deletions

View File

@ -1231,10 +1231,10 @@ target/arm/imx233/creative-zenxfi2/powermgmt-zenxfi2.c
#endif
#ifdef CREATIVE_ZENXFI3
drivers/mpr121.c
#ifndef BOOTLOADER
target/arm/imx233/fmradio-imx233.c
#endif
target/arm/imx233/creative-zenxfi3/mpr121-zenxfi3.c
target/arm/imx233/creative-zenxfi3/backlight-zenxfi3.c
target/arm/imx233/creative-zenxfi3/lcd-zenxfi3.c
target/arm/imx233/creative-zenxfi3/button-zenxfi3.c

View File

@ -1,350 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2012 Amaury Pouly
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/** Driver for the Freescale MPR121 Capacitive Proximity Sensor */
#include "system.h"
#include "mpr121.h"
#include "i2c.h"
/* Touch status: EL{0,7} */
#define REG_TOUCH_STATUS 0x00
#define REG_TOUCH_STATUS__ELE(x) (1 << (x))
/* Touch status: EL{8-11,prox}, overcurrent */
#define REG_TOUCH_STATUS2 0x01
#define REG_TOUCH_STATUS2__ELE(x) (1 << ((x) - 8))
#define REG_TOUCH_STATUS2__ELEPROX (1 << 4)
#define REG_TOUCH_STATUS2__OVCF (1 << 7)
/* Out of range: EL{0,7} */
#define REG_OOR_STATUS 0x02
#define REG_OOR_STATUS__ELE(x) (1 << (x))
/* Out of range: EL{8-11,prox}, autoconf err */
#define REG_OOR_STATUS2 0x03
#define REG_OOR_STATUS2__ELE(x) (1 << (x))
#define REG_OOR_STATUS2__ELEPROX (1 << 4)
#define REG_OOR_STATUS2__ACFF (1 << 6)
#define REG_OOR_STATUS2__ARFF (1 << 7)
/* Electrode X filtered data LSB */
#define REG_EFDxLB(x) (0x04 + 0x02 * (x))
/* Electrode X filtered data MSB */
#define REG_EFDxHB(x) (0x05 + 0x02 * (x))
/* Proximity electrode X filtered data LSB */
#define REG_EFDPROXLB 0x1c
/* Proximity electrode X filtered data MSB */
#define REG_EFDPROXHB 0x1d
/* Electrode baseline value */
#define REG_ExBV(x) (0x1e + (x))
/* Proximity electrode baseline value */
#define REG_EPROXBV 0x2a
/* Max Half Delta Rising */
#define REG_MHDR 0x2b
/* Noise Half Delta Rising */
#define REG_NHDR 0x2c
/* Noise Count Limit Rising */
#define REG_NCLR 0x2d
/* Filter Delay Limit Rising */
#define REG_FDLR 0x2e
/* Max Half Delta Falling */
#define REG_MHDF 0x2f
/* Noise Half Delta Falling */
#define REG_NHDF 0x30
/* Noise Count Limit Falling */
#define REG_NCLF 0x31
/* Filter Delay Limit Falling */
#define REG_FDLF 0x32
/* Noise Half Delta Touched */
#define REG_NHDT 0x33
/* Noise Count Limit Touched */
#define REG_NCLT 0x34
/* Filter Delay Limit Touched */
#define REG_FDLT 0x35
/* Proximity Max Half Delta Rising */
#define REG_MHDPROXR 0x36
/* Proximity Noise Half Delta Rising */
#define REG_NHDPROXR 0x37
/* Proximity Noise Count Limit Rising */
#define REG_NCLPROXR 0x38
/* Proximity Filter Delay Limit Rising */
#define REG_FDLPROXR 0x39
/* Proximity Max Half Delta Falling */
#define REG_MHDPROXF 0x3a
/* Proximity Noise Half Delta Falling */
#define REG_NHDPROXF 0x3b
/* Proximity Noise Count Limit Falling */
#define REG_NCLPROXF 0x3c
/* Proximity Filter Delay Limit Falling */
#define REG_FDLPROXF 0x3d
/* Proximity Noise Half Delta Touched */
#define REG_NHDPROXT 0x3e
/* Proximity Noise Count Limit Touched */
#define REG_NCLPROXT 0x3f
/* Proximity Filter Delay Limit Touched */
#define REG_FDLPROXT 0x40
/* Eletrode Touch Threshold */
#define REG_ExTTH(x) (0x41 + 2 * (x))
/* Eletrode Release Threshold */
#define REG_ExRTH(x) (0x42 + 2 * (x))
/* Proximity Eletrode Touch Threshold */
#define REG_EPROXTTH 0x59
/* Proximity Eletrode Release Threshold */
#define REG_EPROXRTH 0x5a
/* Debounce Control */
#define REG_DEBOUNCE 0x5b
#define REG_DEBOUNCE__DR(dr) ((dr) << 4)
#define REG_DEBOUNCE__DT(dt) (dt)
/* Analog Front End Configuration */
#define REG_AFE 0x5c
#define REG_AFE__CDC(cdc) (cdc)
#define REG_AFE__FFI(ffi) ((ffi) << 6)
/* Filter Configuration */
#define REG_FILTER 0x5d
#define REG_FILTER__ESI(esi) (esi)
#define REG_FILTER__SFI(sfi) ((sfi) << 3)
#define REG_FILTER__CDT(cdt) ((cdt) << 5)
/* Electrode Configuration */
#define REG_ELECTRODE 0x5e
#define REG_ELECTRODE__ELE_EN(en) (en)
#define REG_ELECTRODE__ELEPROX_EN(en) ((en) << 4)
#define REG_ELECTRODE__CL(cl) ((cl) << 6)
/* Electrode X Current */
#define REG_CDCx(x) (0x5f + (x))
/* Proximity Eletrode X Current */
#define REG_CDCPROX 0x6b
/* Electrode X Charge Time */
#define REG_CDTx(x) (0x6c + (x) / 2)
#define REG_CDTx__CDT0(x) (x)
#define REG_CDTx__CDT1(x) ((x) << 4)
/* Proximity Eletrode X Charge Time */
#define REG_CDTPROX 0x72
/* GPIO Control Register: CTL0{4-11} */
#define REG_GPIO_CTL0 0x73
#define REG_GPIO_CTL0__CTL0x(x) (1 << ((x) - 4))
/* GPIO Control Register: CTL1{4-11} */
#define REG_GPIO_CTL1 0x74
#define REG_GPIO_CTL1__CTL1x(x) (1 << ((x) - 4))
/* GPIO Data Register */
#define REG_GPIO_DATA 0x75
#define REG_GPIO_DATA__DATx(x) (1 << ((x) - 4))
/* GPIO Direction Register */
#define REG_GPIO_DIR 0x76
#define REG_GPIO_DIR__DIRx(x) (1 << ((x) - 4))
/* GPIO Enable Register */
#define REG_GPIO_EN 0x77
#define REG_GPIO_EN__ENx(x) (1 << ((x) - 4))
/* GPIO Data Set Register */
#define REG_GPIO_SET 0x78
#define REG_GPIO_SET__SETx(x) (1 << ((x) - 4))
/* GPIO Data Clear Register */
#define REG_GPIO_CLR 0x79
#define REG_GPIO_CLR__CLRx(x) (1 << ((x) - 4))
/* GPIO Data Toggle Register */
#define REG_GPIO_TOG 0x7a
#define REG_GPIO_TOG__TOGx(x) (1 << ((x) - 4))
/* Auto-Configuration Control 0 */
#define REG_AUTO_CONF 0x7b
#define REG_AUTO_CONF__ACE(ace) (ace)
#define REG_AUTO_CONF__ARE(are) ((are) << 1)
#define REG_AUTO_CONF__BVA(bva) ((bva) << 2)
#define REG_AUTO_CONF__RETRY(retry) ((retry) << 4)
#define REG_AUTO_CONF__FFI(ffi) ((ffi) << 6)
/* Auto-Configuration Control 1 */
#define REG_AUTO_CONF2 0x7c
#define REG_AUTO_CONF2__ACFIE(acfie) (acfie)
#define REG_AUTO_CONF2__ARFIE(arfie) ((arfie) << 1)
#define REG_AUTO_CONF2__OORIE(oorie) ((oorie) << 2)
#define REG_AUTO_CONF2__SCTS(scts) ((scts) << 7)
/* Auto-Configuration Upper-Limit */
#define REG_USL 0x7d
/* Auto-Configuration Lower-Limit */
#define REG_LSL 0x7e
/* Auto-Configuration Target Level */
#define REG_TL 0x7f
/* Soft-Reset */
#define REG_SOFTRESET 0x80
#define REG_SOFTRESET__MAGIC 0x63
/* PWM Control */
#define REG_PWMx(x) (0x81 + ((x) - 4) / 2)
#define REG_PWMx_IS_PWM0(x) (((x) % 2) == 0)
#define REG_PWMx__PWM0(x) (x)
#define REG_PWMx__PWM0_BM 0xf
#define REG_PWMx__PWM1(x) ((x) << 4)
#define REG_PWMx__PWM1_BM 0xf0
static int i2c_addr;
static inline int mpr121_write_reg(uint8_t reg, uint8_t data)
{
return i2c_writemem(i2c_addr, reg, &data, 1);
}
static inline int mpr121_read_reg(uint8_t reg, uint8_t *data)
{
return i2c_readmem(i2c_addr, reg, data, 1);
}
int mpr121_init(int dev_i2c_addr)
{
i2c_addr = dev_i2c_addr;
return 0;
}
int mpr121_soft_reset(void)
{
return mpr121_write_reg(REG_SOFTRESET, REG_SOFTRESET__MAGIC);
}
int mpr121_set_config(struct mpr121_config_t *conf)
{
int ret;
#define safe_write(reg, val) \
do { ret = mpr121_write_reg(reg, val); \
if(ret) return ret; } while(0)
/* stop mode */
safe_write(REG_ELECTRODE, 0);
/* write baseline values */
for(int i = 0; i < ELECTRODE_COUNT; i++)
safe_write(REG_ExBV(i), conf->ele[i].bv);
/* write eleprox bv */
safe_write(REG_EPROXBV, conf->eleprox.bv);
/* write global fields */
safe_write(REG_MHDR, conf->filters.ele.rising.mhd);
safe_write(REG_NHDR, conf->filters.ele.rising.nhd);
safe_write(REG_NCLR, conf->filters.ele.rising.ncl);
safe_write(REG_FDLR, conf->filters.ele.rising.fdl);
safe_write(REG_MHDF, conf->filters.ele.falling.mhd);
safe_write(REG_NHDF, conf->filters.ele.falling.nhd);
safe_write(REG_NCLF, conf->filters.ele.falling.ncl);
safe_write(REG_FDLF, conf->filters.ele.falling.fdl);
safe_write(REG_NHDT, conf->filters.ele.touched.nhd);
safe_write(REG_NCLT, conf->filters.ele.touched.ncl);
safe_write(REG_FDLT, conf->filters.ele.touched.fdl);
safe_write(REG_MHDPROXR, conf->filters.eleprox.rising.mhd);
safe_write(REG_NHDPROXR, conf->filters.eleprox.rising.nhd);
safe_write(REG_NCLPROXR, conf->filters.eleprox.rising.ncl);
safe_write(REG_FDLPROXR, conf->filters.eleprox.rising.fdl);
safe_write(REG_MHDPROXF, conf->filters.eleprox.falling.mhd);
safe_write(REG_NHDPROXF, conf->filters.eleprox.falling.nhd);
safe_write(REG_NCLPROXF, conf->filters.eleprox.falling.ncl);
safe_write(REG_FDLPROXF, conf->filters.eleprox.falling.fdl);
safe_write(REG_NHDPROXT, conf->filters.eleprox.touched.nhd);
safe_write(REG_NCLPROXT, conf->filters.eleprox.touched.ncl);
safe_write(REG_FDLPROXT, conf->filters.eleprox.touched.fdl);
/* touch & release thresholds */
for(int i = 0; i < ELECTRODE_COUNT; i++)
{
safe_write(REG_ExTTH(i), conf->ele[i].tth);
safe_write(REG_ExRTH(i), conf->ele[i].rth);
}
safe_write(REG_EPROXTTH, conf->eleprox.tth);
safe_write(REG_EPROXRTH, conf->eleprox.rth);
/* debounce */
safe_write(REG_DEBOUNCE, REG_DEBOUNCE__DR(conf->debounce.dr) |
REG_DEBOUNCE__DT(conf->debounce.dt));
/* analog-front end and filters */
safe_write(REG_AFE, REG_AFE__CDC(conf->global.cdc) |
REG_AFE__FFI(conf->global.ffi));
safe_write(REG_FILTER, REG_FILTER__CDT(conf->global.cdt) |
REG_FILTER__ESI(conf->global.esi) | REG_FILTER__SFI(conf->global.sfi));
/* electrode charge */
for(int i = 0; i < ELECTRODE_COUNT; i++)
safe_write(REG_CDCx(i), conf->ele[i].cdc);
safe_write(REG_CDCPROX, conf->eleprox.cdc);
for(int i = 0; i < ELECTRODE_COUNT; i += 2)
{
safe_write(REG_CDTx(i), REG_CDTx__CDT0(conf->ele[i].cdt) |
REG_CDTx__CDT1(conf->ele[i+1].cdt));
}
safe_write(REG_CDTPROX, conf->eleprox.cdt);
/* Auto-Configuration */
safe_write(REG_AUTO_CONF, REG_AUTO_CONF__ACE(conf->autoconf.en) |
REG_AUTO_CONF__ARE(conf->autoconf.ren) |
REG_AUTO_CONF__BVA(conf->cal_lock) |
REG_AUTO_CONF__RETRY(conf->autoconf.retry) |
REG_AUTO_CONF__FFI(conf->global.ffi));
safe_write(REG_AUTO_CONF2, REG_AUTO_CONF2__ACFIE(conf->autoconf.acfie) |
REG_AUTO_CONF2__ARFIE(conf->autoconf.arfie) |
REG_AUTO_CONF2__OORIE(conf->autoconf.oorie) |
REG_AUTO_CONF2__SCTS(conf->autoconf.scts));
safe_write(REG_USL, conf->autoconf.usl);
safe_write(REG_LSL, conf->autoconf.lsl);
safe_write(REG_TL, conf->autoconf.tl);
/* electrode configuration */
safe_write(REG_ELECTRODE, REG_ELECTRODE__ELE_EN(conf->ele_en) |
REG_ELECTRODE__ELEPROX_EN(conf->eleprox_en) |
REG_ELECTRODE__CL(conf->cal_lock));
/* gpio config */
uint8_t ctl = 0;
for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
if(ELE_GPIO_CTL0(conf->ele[i].gpio))
ctl |= REG_GPIO_CTL0__CTL0x(i);
safe_write(REG_GPIO_CTL0, ctl);
ctl = 0;
for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
if(ELE_GPIO_CTL1(conf->ele[i].gpio))
ctl |= REG_GPIO_CTL1__CTL1x(i);
safe_write(REG_GPIO_CTL1, ctl);
ctl = 0;
for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
if(ELE_GPIO_DIR(conf->ele[i].gpio))
ctl |= REG_GPIO_DIR__DIRx(i);
safe_write(REG_GPIO_DIR, ctl);
ctl = 0;
for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
if(ELE_GPIO_EN(conf->ele[i].gpio))
ctl |= REG_GPIO_EN__ENx(i);
safe_write(REG_GPIO_EN, ctl);
return 0;
}
int mpr121_set_gpio_output(int ele, int gpio_val)
{
switch(gpio_val)
{
case ELE_GPIO_SET:
return mpr121_write_reg(REG_GPIO_SET, REG_GPIO_SET__SETx(ele));
case ELE_GPIO_CLR:
return mpr121_write_reg(REG_GPIO_CLR, REG_GPIO_CLR__CLRx(ele));
case ELE_GPIO_TOG:
return mpr121_write_reg(REG_GPIO_TOG, REG_GPIO_TOG__TOGx(ele));
default:
return -1;
}
}
int mpr121_set_gpio_pwm(int ele, int pwm)
{
uint8_t reg_val;
int ret = mpr121_read_reg(REG_PWMx(ele), &reg_val);
if(ret) return ret;
if(REG_PWMx_IS_PWM0(ele))
reg_val = (reg_val & ~REG_PWMx__PWM0_BM) | REG_PWMx__PWM0(pwm);
else
reg_val = (reg_val & ~REG_PWMx__PWM1_BM) | REG_PWMx__PWM1(pwm);
return mpr121_write_reg(REG_PWMx(ele), reg_val);
}
int mpr121_get_touch_status(unsigned *status)
{
uint8_t buf[2];
int ret = i2c_readmem(i2c_addr, REG_TOUCH_STATUS, buf, 2);
if(!ret && status)
*status = buf[0] | buf[1];
return ret;
}

View File

@ -18,153 +18,173 @@
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __MPR121_H__
#define __MPR121_H__
/** Driver for the Freescale MPR121 Capacitive Proximity Sensor */
/** Registers for the Freescale MPR121 Capacitive Proximity Sensor */
#include "system.h"
#define ELECTRODE_COUNT 12
#define ELE_GPIO_FIRST 4
#define ELE_GPIO_LAST 11
/* Touch status: EL{0,7} */
#define REG_TOUCH_STATUS 0x00
#define REG_TOUCH_STATUS__ELE(x) (1 << (x))
/* Touch status: EL{8-11,prox}, overcurrent */
#define REG_TOUCH_STATUS2 0x01
#define REG_TOUCH_STATUS2__ELE(x) (1 << ((x) - 8))
#define REG_TOUCH_STATUS2__ELEPROX (1 << 4)
#define REG_TOUCH_STATUS2__OVCF (1 << 7)
/* Out of range: EL{0,7} */
#define REG_OOR_STATUS 0x02
#define REG_OOR_STATUS__ELE(x) (1 << (x))
/* Out of range: EL{8-11,prox}, autoconf err */
#define REG_OOR_STATUS2 0x03
#define REG_OOR_STATUS2__ELE(x) (1 << (x))
#define REG_OOR_STATUS2__ELEPROX (1 << 4)
#define REG_OOR_STATUS2__ACFF (1 << 6)
#define REG_OOR_STATUS2__ARFF (1 << 7)
/* Electrode X filtered data LSB */
#define REG_EFDxLB(x) (0x04 + 0x02 * (x))
/* Electrode X filtered data MSB */
#define REG_EFDxHB(x) (0x05 + 0x02 * (x))
/* Proximity electrode X filtered data LSB */
#define REG_EFDPROXLB 0x1c
/* Proximity electrode X filtered data MSB */
#define REG_EFDPROXHB 0x1d
/* Electrode baseline value */
#define REG_ExBV(x) (0x1e + (x))
/* Proximity electrode baseline value */
#define REG_EPROXBV 0x2a
/* Max Half Delta Rising */
#define REG_MHDR 0x2b
/* Noise Half Delta Rising */
#define REG_NHDR 0x2c
/* Noise Count Limit Rising */
#define REG_NCLR 0x2d
/* Filter Delay Limit Rising */
#define REG_FDLR 0x2e
/* Max Half Delta Falling */
#define REG_MHDF 0x2f
/* Noise Half Delta Falling */
#define REG_NHDF 0x30
/* Noise Count Limit Falling */
#define REG_NCLF 0x31
/* Filter Delay Limit Falling */
#define REG_FDLF 0x32
/* Noise Half Delta Touched */
#define REG_NHDT 0x33
/* Noise Count Limit Touched */
#define REG_NCLT 0x34
/* Filter Delay Limit Touched */
#define REG_FDLT 0x35
/* Proximity Max Half Delta Rising */
#define REG_MHDPROXR 0x36
/* Proximity Noise Half Delta Rising */
#define REG_NHDPROXR 0x37
/* Proximity Noise Count Limit Rising */
#define REG_NCLPROXR 0x38
/* Proximity Filter Delay Limit Rising */
#define REG_FDLPROXR 0x39
/* Proximity Max Half Delta Falling */
#define REG_MHDPROXF 0x3a
/* Proximity Noise Half Delta Falling */
#define REG_NHDPROXF 0x3b
/* Proximity Noise Count Limit Falling */
#define REG_NCLPROXF 0x3c
/* Proximity Filter Delay Limit Falling */
#define REG_FDLPROXF 0x3d
/* Proximity Noise Half Delta Touched */
#define REG_NHDPROXT 0x3e
/* Proximity Noise Count Limit Touched */
#define REG_NCLPROXT 0x3f
/* Proximity Filter Delay Limit Touched */
#define REG_FDLPROXT 0x40
/* Eletrode Touch Threshold */
#define REG_ExTTH(x) (0x41 + 2 * (x))
/* Eletrode Release Threshold */
#define REG_ExRTH(x) (0x42 + 2 * (x))
/* Proximity Eletrode Touch Threshold */
#define REG_EPROXTTH 0x59
/* Proximity Eletrode Release Threshold */
#define REG_EPROXRTH 0x5a
/* Debounce Control */
#define REG_DEBOUNCE 0x5b
#define REG_DEBOUNCE__DR(dr) ((dr) << 4)
#define REG_DEBOUNCE__DT(dt) (dt)
/* Analog Front End Configuration */
#define REG_AFE 0x5c
#define REG_AFE__CDC(cdc) (cdc)
#define REG_AFE__FFI(ffi) ((ffi) << 6)
/* Filter Configuration */
#define REG_FILTER 0x5d
#define REG_FILTER__ESI(esi) (esi)
#define REG_FILTER__SFI(sfi) ((sfi) << 3)
#define REG_FILTER__CDT(cdt) ((cdt) << 5)
/* Electrode Configuration */
#define REG_ELECTRODE 0x5e
#define REG_ELECTRODE__ELE_EN(en) (en)
#define REG_ELECTRODE__ELEPROX_EN(en) ((en) << 4)
#define REG_ELECTRODE__CL(cl) ((cl) << 6)
/* Electrode X Current */
#define REG_CDCx(x) (0x5f + (x))
/* Proximity Eletrode X Current */
#define REG_CDCPROX 0x6b
/* Electrode X Charge Time */
#define REG_CDTx(x) (0x6c + (x) / 2)
#define REG_CDTx__CDT0(x) (x)
#define REG_CDTx__CDT1(x) ((x) << 4)
/* Proximity Eletrode X Charge Time */
#define REG_CDTPROX 0x72
/* GPIO Control Register: CTL0{4-11} */
#define REG_GPIO_CTL0 0x73
#define REG_GPIO_CTL0__CTL0x(x) (1 << ((x) - 4))
/* GPIO Control Register: CTL1{4-11} */
#define REG_GPIO_CTL1 0x74
#define REG_GPIO_CTL1__CTL1x(x) (1 << ((x) - 4))
/* GPIO Data Register */
#define REG_GPIO_DATA 0x75
#define REG_GPIO_DATA__DATx(x) (1 << ((x) - 4))
/* GPIO Direction Register */
#define REG_GPIO_DIR 0x76
#define REG_GPIO_DIR__DIRx(x) (1 << ((x) - 4))
/* GPIO Enable Register */
#define REG_GPIO_EN 0x77
#define REG_GPIO_EN__ENx(x) (1 << ((x) - 4))
/* GPIO Data Set Register */
#define REG_GPIO_SET 0x78
#define REG_GPIO_SET__SETx(x) (1 << ((x) - 4))
/* GPIO Data Clear Register */
#define REG_GPIO_CLR 0x79
#define REG_GPIO_CLR__CLRx(x) (1 << ((x) - 4))
/* GPIO Data Toggle Register */
#define REG_GPIO_TOG 0x7a
#define REG_GPIO_TOG__TOGx(x) (1 << ((x) - 4))
/* Auto-Configuration Control 0 */
#define REG_AUTO_CONF 0x7b
#define REG_AUTO_CONF__ACE(ace) (ace)
#define REG_AUTO_CONF__ARE(are) ((are) << 1)
#define REG_AUTO_CONF__BVA(bva) ((bva) << 2)
#define REG_AUTO_CONF__RETRY(retry) ((retry) << 4)
#define REG_AUTO_CONF__FFI(ffi) ((ffi) << 6)
/* Auto-Configuration Control 1 */
#define REG_AUTO_CONF2 0x7c
#define REG_AUTO_CONF2__ACFIE(acfie) (acfie)
#define REG_AUTO_CONF2__ARFIE(arfie) ((arfie) << 1)
#define REG_AUTO_CONF2__OORIE(oorie) ((oorie) << 2)
#define REG_AUTO_CONF2__SCTS(scts) ((scts) << 7)
/* Auto-Configuration Upper-Limit */
#define REG_USL 0x7d
/* Auto-Configuration Lower-Limit */
#define REG_LSL 0x7e
/* Auto-Configuration Target Level */
#define REG_TL 0x7f
/* Soft-Reset */
#define REG_SOFTRESET 0x80
#define REG_SOFTRESET__MAGIC 0x63
/* PWM Control */
#define REG_PWMx(x) (0x81 + ((x) - 4) / 2)
#define REG_PWMx_IS_PWM0(x) (((x) % 2) == 0)
#define REG_PWMx__PWM0(x) (x)
#define REG_PWMx__PWM0_BM 0xf
#define REG_PWMx__PWM1(x) ((x) << 4)
#define REG_PWMx__PWM1_BM 0xf0
/* gpio config (encoding: [0]=en,[1]=dir,[2]=ctl[1],[3]=ctl[0]) */
#define ELE_GPIO_DISABLE 0
#define ELE_GPIO_INPUT 1
#define ELE_GPIO_INPUT_PULLDOWN 9 /* input with pull-down */
#define ELE_GPIO_INPUT_PULLUP 13 /* input with pull-up */
#define ELE_GPIO_OUTPUT 3
#define ELE_GPIO_OUTPUT_OPEN 11 /* open drain low-side */
#define ELE_GPIO_OUTPUT_OPEN_LED 15 /* open drain high-side (led driver) */
/* internal use */
#define ELE_GPIO_EN(val) ((val) & 1)
#define ELE_GPIO_DIR(val) (((val) >> 1) & 1)
#define ELE_GPIO_CTL0(val) (((val) >> 3) & 1)
#define ELE_GPIO_CTL1(val) (((val) >> 1) & 1)
struct mpr121_electrode_config_t
{
uint8_t bv; /* baseline value */
uint8_t tth; /* touch threshold */
uint8_t rth; /* release threshold */
uint8_t cdc; /* charge current (optional if auto-conf) */
uint8_t cdt; /* charge time (optional if auto-conf) */
int gpio; /* gpio config */
};
struct mpr121_baseline_filter_config_t
{
uint8_t mhd; /* max half delta (except for touched) */
uint8_t nhd; /* noise half delta */
uint8_t ncl; /* noise count limit */
uint8_t fdl; /* filter delay count limit */
};
struct mpr121_baseline_filters_config_t
{
struct mpr121_baseline_filter_config_t rising;
struct mpr121_baseline_filter_config_t falling;
struct mpr121_baseline_filter_config_t touched;
};
struct mpr121_debounce_config_t
{
uint8_t dt; /* debounce count for touch */
uint8_t dr; /* debounce count for release */
};
/* first filter iterations */
#define FFI_6_SAMPLES 0
#define FFI_10_SAMPLES 1
#define FFI_18_SAMPLES 2
#define FFI_34_SAMPLES 3
/* charge discharge current */
#define CDC_DISABLE 0
#define CDC_uA(ua) (ua)
/* charge discharge time */
#define CDT_DISABLE 0
#define CDT_log_us(lus) (lus) /* actual value = 2^{us-2} µs */
/* second filter iterations */
#define SFI_4_SAMPLES 0
#define SFI_6_SAMPLES 1
#define SFI_10_SAMPLES 2
#define SFI_18_SAMPLES 3
/* Eletrode sample interval */
#define ESI_log_ms(lms) (lms) /* actual value = 2^{lms} ms */
struct mpr121_global_config_t
{
uint8_t ffi; /* first filter iterations */
uint8_t cdc; /* global charge discharge current */
uint8_t cdt; /* global charge discharge time */
uint8_t sfi; /* second first iterations */
uint8_t esi; /* electrode sample interval */
};
#define RETRY_NEVER 0
#define RETRY_2_TIMES 1
#define RETRY_4_TIMES 2
#define RETRY_8_TIMES 3
struct mpr121_auto_config_t
{
bool en; /* auto-conf enable */
bool ren; /* auto-reconf enable */
uint8_t retry; /* retry count */
bool scts; /* skip charge time search */
uint8_t usl; /* upper-side limit */
uint8_t lsl; /* lower-side limit */
uint8_t tl; /* target level */
bool acfie; /* auto-conf fail interrupt en */
bool arfie; /* auto-reconf fail interrupt en */
bool oorie; /* out of range interrupt en */
};
/* electrode mode */
#define ELE_DISABLE 0
#define ELE_EN0_x(x) ((x) + 1)
/* eleprox mode */
#define ELEPROX_DISABLE 0
#define ELEPROX_EN0_1 1
#define ELEPROX_EN0_3 2
#define ELEPROX_EN0_11 3
/* calibration lock */
#define CL_SLOW_TRACK 0
#define CL_DISABLE 1
#define CL_TRACK 2
#define CL_FAST_TRACK 3
struct mpr121_config_t
{
struct mpr121_electrode_config_t ele[ELECTRODE_COUNT];
struct mpr121_electrode_config_t eleprox;
struct
{
struct mpr121_baseline_filters_config_t ele;
struct mpr121_baseline_filters_config_t eleprox;
}filters;
struct mpr121_debounce_config_t debounce;
struct mpr121_global_config_t global;
struct mpr121_auto_config_t autoconf;
uint8_t ele_en; /* eletroce mode */
uint8_t eleprox_en; /* proximity mode */
uint8_t cal_lock; /* calibration lock */
};
/* gpio value */
#define ELE_GPIO_CLR 0
#define ELE_GPIO_SET 1
#define ELE_GPIO_TOG 2
/* pwm value */
#define ELE_PWM_DISABLE 0
#define ELE_PWM_DUTY(x) (x)
#define ELE_PWM_MIN_DUTY 1
#define ELE_PWM_MAX_DUTY 15
int mpr121_init(int dev_i2c_addr);
int mpr121_soft_reset(void);
int mpr121_set_config(struct mpr121_config_t *conf);
/* gpios are only implemented for electrode>=4 */
int mpr121_set_gpio_output(int ele, int gpio_val);
int mpr121_set_gpio_pwm(int ele, int pwm);
/* get electrode status (bitmap) */
int mpr121_get_touch_status(unsigned *status);
#endif /* __MPR121_H__ */

View File

@ -25,7 +25,7 @@
#include "backlight.h"
#include "backlight-target.h"
#include "pwm-imx233.h"
#include "mpr121.h"
#include "mpr121-zenxfi3.h"
void backlight_hw_brightness(int brightness)
{

View File

@ -18,16 +18,10 @@
* KIND, either express or implied.
*
****************************************************************************/
#include "button-target.h"
#include "system.h"
#include "system-target.h"
#include "pinctrl-imx233.h"
#include "power-imx233.h"
#include "tick.h"
#include "button-imx233.h"
#include "string.h"
#include "usb.h"
#include "backlight.h"
#include "mpr121.h"
#include "mpr121-zenxfi3.h"
#define I_VDDIO 0 /* index in the table */
@ -42,7 +36,8 @@ struct imx233_button_map_t imx233_button_map[] =
IMX233_BUTTON_(END, END(), "")
};
static struct mpr121_config_t config =
/* MPR121 configuration, mostly extracted from OF */
static struct mpr121_config_t mpr121_config =
{
.ele =
{
@ -73,74 +68,11 @@ static struct mpr121_config_t config =
.cal_lock = CL_TRACK
};
#define MPR121_INTERRUPT 1
static int touchpad_btns = 0;
static long mpr121_stack[DEFAULT_STACK_SIZE/sizeof(long)];
static const char mpr121_thread_name[] = "mpr121";
static struct event_queue mpr121_queue;
static void mpr121_irq_cb(int bank, int pin, intptr_t user)
{
(void) bank;
(void) pin;
(void) user;
/* the callback will not be fired until interrupt is enabled back so
* the queue will not overflow or contain multiple MPR121_INTERRUPT events */
queue_post(&mpr121_queue, MPR121_INTERRUPT, 0);
}
static void mpr121_thread(void)
{
struct queue_event ev;
while(1)
{
queue_wait(&mpr121_queue, &ev);
/* handle usb connect and ignore all messages except rmi interrupts */
if(ev.id == SYS_USB_CONNECTED)
{
usb_acknowledge(SYS_USB_CONNECTED_ACK);
continue;
}
else if(ev.id != MPR121_INTERRUPT)
continue;
/* clear interrupt and get status */
unsigned status;
touchpad_btns = 0;
if(!mpr121_get_touch_status(&status))
{
/* ELE3: up
* ELE4: back
* ELE5: menu
* ELE6: down
* ELE7: play */
if(status & 0x8) touchpad_btns |= BUTTON_UP;
if(status & 0x10) touchpad_btns |= BUTTON_BACK;
if(status & 0x20) touchpad_btns |= BUTTON_MENU;
if(status & 0x40) touchpad_btns |= BUTTON_DOWN;
if(status & 0x80) touchpad_btns |= BUTTON_PLAY;
}
/* enable interrupt */
imx233_pinctrl_setup_irq(0, 18, true, true, false, &mpr121_irq_cb, 0);
}
}
/* B0P18 is #IRQ line of the touchpad */
void button_init_device(void)
{
mpr121_init(0xb4);
mpr121_soft_reset();
mpr121_set_config(&config);
queue_init(&mpr121_queue, true);
create_thread(mpr121_thread, mpr121_stack, sizeof(mpr121_stack), 0,
mpr121_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU));
/* enable interrupt */
imx233_pinctrl_acquire(0, 18, "mpr121_int");
imx233_pinctrl_set_function(0, 18, PINCTRL_FUNCTION_GPIO);
imx233_pinctrl_enable_gpio(0, 18, false);
imx233_pinctrl_setup_irq(0, 18, true, true, false, &mpr121_irq_cb, 0);
mpr121_init();
mpr121_set_config(&mpr121_config);
/* generic part */
imx233_button_init();
}
@ -151,7 +83,6 @@ int button_read_device(void)
* for one second after hold is released */
static int power_ignore_counter = 0;
static bool old_hold;
/* light handling */
bool hold = button_hold();
if(hold != old_hold)
{
@ -159,6 +90,20 @@ int button_read_device(void)
if(!hold)
power_ignore_counter = HZ;
}
/* interpret touchpad status */
unsigned status = mpr121_get_touch_status();
unsigned touchpad_btns = 0;
/* ELE3: up
* ELE4: back
* ELE5: menu
* ELE6: down
* ELE7: play */
if(status & 0x8) touchpad_btns |= BUTTON_UP;
if(status & 0x10) touchpad_btns |= BUTTON_BACK;
if(status & 0x20) touchpad_btns |= BUTTON_MENU;
if(status & 0x40) touchpad_btns |= BUTTON_DOWN;
if(status & 0x80) touchpad_btns |= BUTTON_PLAY;
/* feed it to generic code */
int res = imx233_button_read(touchpad_btns);
if(power_ignore_counter > 0)
{

View File

@ -0,0 +1,236 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2012 Amaury Pouly
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/** Driver for the Freescale MPR121 Capacitive Proximity Sensor */
#include "system.h"
#include "kernel.h"
#include "usb.h"
#include "mpr121.h"
#include "mpr121-zenxfi3.h"
#include "i2c-imx233.h"
#include "pinctrl-imx233.h"
#define MPR121_I2C_ADDR 0xb4
/* NOTE on the architecture of the driver
*
* All non-time-critical operations (setup, gpio/pwm changes) are done with
* blocking i2c transfers to make the code simpler. Since reading the touch
* status is time critical, it is done asynchronously: when the IRQ pin is
* asserted, it will disable IRQ pin sensing and trigger an asynchronous i2c
* transfer to read touch status. When the transfer finishes, the driver will
* renable IRQ pin sensing. */
static unsigned touch_status = 0; /* touch bitmask as reported by mpr121 */
static struct imx233_i2c_xfer_t read_status_xfer; /* async transfer to read touch status */
static uint8_t read_status_sel_reg; /* buffer for async transfer operation */
static uint8_t read_status_buf[2]; /* buffer for async transfer operation */
static void mpr121_irq_cb(int bank, int pin, intptr_t user);
static void touch_status_i2c_cb(struct imx233_i2c_xfer_t *xfer, enum imx233_i2c_error_t status)
{
(void) xfer;
(void) status;
/* put status in the global variable */
touch_status = read_status_buf[0] | read_status_buf[1] << 8;
/* start sensing IRQ pin again */
imx233_pinctrl_setup_irq(0, 18, true, true, false, &mpr121_irq_cb, 0);
}
void mpr121_irq_cb(int bank, int pin, intptr_t user)
{
(void) bank;
(void) pin;
(void) user;
/* NOTE the callback will not be fired until interrupt is enabled back.
*
* now setup an asynchronous i2c transfer to read touch status register,
* this is a readmem operation with a first stage to select register
* and a second stage to read status (2 bytes) */
read_status_sel_reg = REG_TOUCH_STATUS;
read_status_xfer.next = NULL;
read_status_xfer.fast_mode = true;
read_status_xfer.dev_addr = MPR121_I2C_ADDR;
read_status_xfer.mode = I2C_READ;
read_status_xfer.count[0] = 1; /* set touch status register address */
read_status_xfer.data[0] = &read_status_sel_reg;
read_status_xfer.count[1] = 2;
read_status_xfer.data[1] = &read_status_buf;
read_status_xfer.tmo_ms = 1000;
read_status_xfer.callback = &touch_status_i2c_cb;
imx233_i2c_transfer(&read_status_xfer);
}
static inline int mpr121_write_reg(uint8_t reg, uint8_t data)
{
return i2c_writemem(MPR121_I2C_ADDR, reg, &data, 1);
}
static inline int mpr121_read_reg(uint8_t reg, uint8_t *data)
{
return i2c_readmem(MPR121_I2C_ADDR, reg, data, 1);
}
void mpr121_init(void)
{
/* soft reset */
mpr121_write_reg(REG_SOFTRESET, REG_SOFTRESET__MAGIC);
/* enable interrupt */
imx233_pinctrl_acquire(0, 18, "mpr121_int");
imx233_pinctrl_set_function(0, 18, PINCTRL_FUNCTION_GPIO);
imx233_pinctrl_enable_gpio(0, 18, false);
imx233_pinctrl_setup_irq(0, 18, true, true, false, &mpr121_irq_cb, 0);
}
void mpr121_set_config(struct mpr121_config_t *conf)
{
/* stop mode */
mpr121_write_reg(REG_ELECTRODE, 0);
/* write baseline values */
for(int i = 0; i < ELECTRODE_COUNT; i++)
mpr121_write_reg(REG_ExBV(i), conf->ele[i].bv);
/* write eleprox bv */
mpr121_write_reg(REG_EPROXBV, conf->eleprox.bv);
/* write global fields */
mpr121_write_reg(REG_MHDR, conf->filters.ele.rising.mhd);
mpr121_write_reg(REG_NHDR, conf->filters.ele.rising.nhd);
mpr121_write_reg(REG_NCLR, conf->filters.ele.rising.ncl);
mpr121_write_reg(REG_FDLR, conf->filters.ele.rising.fdl);
mpr121_write_reg(REG_MHDF, conf->filters.ele.falling.mhd);
mpr121_write_reg(REG_NHDF, conf->filters.ele.falling.nhd);
mpr121_write_reg(REG_NCLF, conf->filters.ele.falling.ncl);
mpr121_write_reg(REG_FDLF, conf->filters.ele.falling.fdl);
mpr121_write_reg(REG_NHDT, conf->filters.ele.touched.nhd);
mpr121_write_reg(REG_NCLT, conf->filters.ele.touched.ncl);
mpr121_write_reg(REG_FDLT, conf->filters.ele.touched.fdl);
mpr121_write_reg(REG_MHDPROXR, conf->filters.eleprox.rising.mhd);
mpr121_write_reg(REG_NHDPROXR, conf->filters.eleprox.rising.nhd);
mpr121_write_reg(REG_NCLPROXR, conf->filters.eleprox.rising.ncl);
mpr121_write_reg(REG_FDLPROXR, conf->filters.eleprox.rising.fdl);
mpr121_write_reg(REG_MHDPROXF, conf->filters.eleprox.falling.mhd);
mpr121_write_reg(REG_NHDPROXF, conf->filters.eleprox.falling.nhd);
mpr121_write_reg(REG_NCLPROXF, conf->filters.eleprox.falling.ncl);
mpr121_write_reg(REG_FDLPROXF, conf->filters.eleprox.falling.fdl);
mpr121_write_reg(REG_NHDPROXT, conf->filters.eleprox.touched.nhd);
mpr121_write_reg(REG_NCLPROXT, conf->filters.eleprox.touched.ncl);
mpr121_write_reg(REG_FDLPROXT, conf->filters.eleprox.touched.fdl);
/* touch & release thresholds */
for(int i = 0; i < ELECTRODE_COUNT; i++)
{
mpr121_write_reg(REG_ExTTH(i), conf->ele[i].tth);
mpr121_write_reg(REG_ExRTH(i), conf->ele[i].rth);
}
mpr121_write_reg(REG_EPROXTTH, conf->eleprox.tth);
mpr121_write_reg(REG_EPROXRTH, conf->eleprox.rth);
/* debounce */
mpr121_write_reg(REG_DEBOUNCE, REG_DEBOUNCE__DR(conf->debounce.dr) |
REG_DEBOUNCE__DT(conf->debounce.dt));
/* analog-front end and filters */
mpr121_write_reg(REG_AFE, REG_AFE__CDC(conf->global.cdc) |
REG_AFE__FFI(conf->global.ffi));
mpr121_write_reg(REG_FILTER, REG_FILTER__CDT(conf->global.cdt) |
REG_FILTER__ESI(conf->global.esi) | REG_FILTER__SFI(conf->global.sfi));
/* electrode charge */
for(int i = 0; i < ELECTRODE_COUNT; i++)
mpr121_write_reg(REG_CDCx(i), conf->ele[i].cdc);
mpr121_write_reg(REG_CDCPROX, conf->eleprox.cdc);
for(int i = 0; i < ELECTRODE_COUNT; i += 2)
{
mpr121_write_reg(REG_CDTx(i), REG_CDTx__CDT0(conf->ele[i].cdt) |
REG_CDTx__CDT1(conf->ele[i+1].cdt));
}
mpr121_write_reg(REG_CDTPROX, conf->eleprox.cdt);
/* Auto-Configuration */
mpr121_write_reg(REG_AUTO_CONF, REG_AUTO_CONF__ACE(conf->autoconf.en) |
REG_AUTO_CONF__ARE(conf->autoconf.ren) |
REG_AUTO_CONF__BVA(conf->cal_lock) |
REG_AUTO_CONF__RETRY(conf->autoconf.retry) |
REG_AUTO_CONF__FFI(conf->global.ffi));
mpr121_write_reg(REG_AUTO_CONF2, REG_AUTO_CONF2__ACFIE(conf->autoconf.acfie) |
REG_AUTO_CONF2__ARFIE(conf->autoconf.arfie) |
REG_AUTO_CONF2__OORIE(conf->autoconf.oorie) |
REG_AUTO_CONF2__SCTS(conf->autoconf.scts));
mpr121_write_reg(REG_USL, conf->autoconf.usl);
mpr121_write_reg(REG_LSL, conf->autoconf.lsl);
mpr121_write_reg(REG_TL, conf->autoconf.tl);
/* electrode configuration */
mpr121_write_reg(REG_ELECTRODE, REG_ELECTRODE__ELE_EN(conf->ele_en) |
REG_ELECTRODE__ELEPROX_EN(conf->eleprox_en) |
REG_ELECTRODE__CL(conf->cal_lock));
/* gpio config */
uint8_t ctl = 0;
for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
if(ELE_GPIO_CTL0(conf->ele[i].gpio))
ctl |= REG_GPIO_CTL0__CTL0x(i);
mpr121_write_reg(REG_GPIO_CTL0, ctl);
ctl = 0;
for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
if(ELE_GPIO_CTL1(conf->ele[i].gpio))
ctl |= REG_GPIO_CTL1__CTL1x(i);
mpr121_write_reg(REG_GPIO_CTL1, ctl);
ctl = 0;
for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
if(ELE_GPIO_DIR(conf->ele[i].gpio))
ctl |= REG_GPIO_DIR__DIRx(i);
mpr121_write_reg(REG_GPIO_DIR, ctl);
ctl = 0;
for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
if(ELE_GPIO_EN(conf->ele[i].gpio))
ctl |= REG_GPIO_EN__ENx(i);
mpr121_write_reg(REG_GPIO_EN, ctl);
}
void mpr121_set_gpio_output(int ele, int gpio_val)
{
switch(gpio_val)
{
case ELE_GPIO_SET:
mpr121_write_reg(REG_GPIO_SET, REG_GPIO_SET__SETx(ele));
break;
case ELE_GPIO_CLR:
mpr121_write_reg(REG_GPIO_CLR, REG_GPIO_CLR__CLRx(ele));
break;
case ELE_GPIO_TOG:
mpr121_write_reg(REG_GPIO_TOG, REG_GPIO_TOG__TOGx(ele));
break;
default:
break;
}
}
void mpr121_set_gpio_pwm(int ele, int pwm)
{
uint8_t reg_val;
mpr121_read_reg(REG_PWMx(ele), &reg_val);
if(REG_PWMx_IS_PWM0(ele))
reg_val = (reg_val & ~REG_PWMx__PWM0_BM) | REG_PWMx__PWM0(pwm);
else
reg_val = (reg_val & ~REG_PWMx__PWM1_BM) | REG_PWMx__PWM1(pwm);
mpr121_write_reg(REG_PWMx(ele), reg_val);
}
unsigned mpr121_get_touch_status(void)
{
return touch_status;
}

View File

@ -0,0 +1,176 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2016 Amaury Pouly
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef __MPR121_ZENXFI3_H__
#define __MPR121_ZENXFI3_H__
/** Driver for the Freescale MPR121 Capacitive Proximity Sensor */
#include "system.h"
#define ELECTRODE_COUNT 12
#define ELE_GPIO_FIRST 4
#define ELE_GPIO_LAST 11
/* gpio config (encoding: [0]=en,[1]=dir,[2]=ctl[1],[3]=ctl[0]) */
#define ELE_GPIO_DISABLE 0
#define ELE_GPIO_INPUT 1
#define ELE_GPIO_INPUT_PULLDOWN 9 /* input with pull-down */
#define ELE_GPIO_INPUT_PULLUP 13 /* input with pull-up */
#define ELE_GPIO_OUTPUT 3
#define ELE_GPIO_OUTPUT_OPEN 11 /* open drain low-side */
#define ELE_GPIO_OUTPUT_OPEN_LED 15 /* open drain high-side (led driver) */
/* internal use */
#define ELE_GPIO_EN(val) ((val) & 1)
#define ELE_GPIO_DIR(val) (((val) >> 1) & 1)
#define ELE_GPIO_CTL0(val) (((val) >> 3) & 1)
#define ELE_GPIO_CTL1(val) (((val) >> 1) & 1)
struct mpr121_electrode_config_t
{
uint8_t bv; /* baseline value */
uint8_t tth; /* touch threshold */
uint8_t rth; /* release threshold */
uint8_t cdc; /* charge current (optional if auto-conf) */
uint8_t cdt; /* charge time (optional if auto-conf) */
int gpio; /* gpio config */
};
struct mpr121_baseline_filter_config_t
{
uint8_t mhd; /* max half delta (except for touched) */
uint8_t nhd; /* noise half delta */
uint8_t ncl; /* noise count limit */
uint8_t fdl; /* filter delay count limit */
};
struct mpr121_baseline_filters_config_t
{
struct mpr121_baseline_filter_config_t rising;
struct mpr121_baseline_filter_config_t falling;
struct mpr121_baseline_filter_config_t touched;
};
struct mpr121_debounce_config_t
{
uint8_t dt; /* debounce count for touch */
uint8_t dr; /* debounce count for release */
};
/* first filter iterations */
#define FFI_6_SAMPLES 0
#define FFI_10_SAMPLES 1
#define FFI_18_SAMPLES 2
#define FFI_34_SAMPLES 3
/* charge discharge current */
#define CDC_DISABLE 0
#define CDC_uA(ua) (ua)
/* charge discharge time */
#define CDT_DISABLE 0
#define CDT_log_us(lus) (lus) /* actual value = 2^{us-2} µs */
/* second filter iterations */
#define SFI_4_SAMPLES 0
#define SFI_6_SAMPLES 1
#define SFI_10_SAMPLES 2
#define SFI_18_SAMPLES 3
/* Eletrode sample interval */
#define ESI_log_ms(lms) (lms) /* actual value = 2^{lms} ms */
struct mpr121_global_config_t
{
uint8_t ffi; /* first filter iterations */
uint8_t cdc; /* global charge discharge current */
uint8_t cdt; /* global charge discharge time */
uint8_t sfi; /* second first iterations */
uint8_t esi; /* electrode sample interval */
};
#define RETRY_NEVER 0
#define RETRY_2_TIMES 1
#define RETRY_4_TIMES 2
#define RETRY_8_TIMES 3
struct mpr121_auto_config_t
{
bool en; /* auto-conf enable */
bool ren; /* auto-reconf enable */
uint8_t retry; /* retry count */
bool scts; /* skip charge time search */
uint8_t usl; /* upper-side limit */
uint8_t lsl; /* lower-side limit */
uint8_t tl; /* target level */
bool acfie; /* auto-conf fail interrupt en */
bool arfie; /* auto-reconf fail interrupt en */
bool oorie; /* out of range interrupt en */
};
/* electrode mode */
#define ELE_DISABLE 0
#define ELE_EN0_x(x) ((x) + 1)
/* eleprox mode */
#define ELEPROX_DISABLE 0
#define ELEPROX_EN0_1 1
#define ELEPROX_EN0_3 2
#define ELEPROX_EN0_11 3
/* calibration lock */
#define CL_SLOW_TRACK 0
#define CL_DISABLE 1
#define CL_TRACK 2
#define CL_FAST_TRACK 3
struct mpr121_config_t
{
struct mpr121_electrode_config_t ele[ELECTRODE_COUNT];
struct mpr121_electrode_config_t eleprox;
struct
{
struct mpr121_baseline_filters_config_t ele;
struct mpr121_baseline_filters_config_t eleprox;
}filters;
struct mpr121_debounce_config_t debounce;
struct mpr121_global_config_t global;
struct mpr121_auto_config_t autoconf;
uint8_t ele_en; /* eletroce mode */
uint8_t eleprox_en; /* proximity mode */
uint8_t cal_lock; /* calibration lock */
};
/* gpio value */
#define ELE_GPIO_CLR 0
#define ELE_GPIO_SET 1
#define ELE_GPIO_TOG 2
/* pwm value */
#define ELE_PWM_DISABLE 0
#define ELE_PWM_DUTY(x) (x)
#define ELE_PWM_MIN_DUTY 1
#define ELE_PWM_MAX_DUTY 15
void mpr121_init(void);
void mpr121_set_config(struct mpr121_config_t *conf);
/* gpios are only implemented for electrode>=4, use ELE_GPIO_* for value */
void mpr121_set_gpio_output(int ele, int gpio_val);
/* pwm value is between 0 and 15, use ELE_PWM_DISABLE or ELE_PWM_DUTY */
void mpr121_set_gpio_pwm(int ele, int pwm);
/* get electrode status (bitmap)
* NOTE this function merely returns the last electrode status read from the
* device and does not actively ask the device for touch status. */
unsigned mpr121_get_touch_status(void);
#endif /* __MPR121_ZENXFI3_H__ */