Skip to content
Snippets Groups Projects
captouch.c 21.37 KiB
//#include <stdio.h>
//#include <string.h>
#include "esp_log.h"
#include <stdint.h>
#include <freertos/FreeRTOS.h>
#include <freertos/atomic.h>
#include "flow3r_bsp_i2c.h"

#define PETAL_PAD_TIP 0
#define PETAL_PAD_CCW 1
#define PETAL_PAD_CW 2
#define PETAL_PAD_BASE 3

#define CIN CDC_NONE    0
#define CIN_CDC_NEG     1
#define CIN_CDC_POS     2
#define CIN_BIAS        3

#define AFE_INCR_CAP 1000

#if defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4) || defined(CONFIG_BADGE23_HW_GEN_P6)
static const uint8_t top_map[] = {0, 0, 0, 2, 2, 2, 6, 6, 6, 4, 4, 4};
static const uint8_t top_stages = 12;
static const uint8_t bot_map[] = {1, 1, 3, 3, 5, 7, 7, 9, 9, 8, 8, 8};
static const uint8_t bot_stages = 12;
static const uint8_t bot_stage_config[] = {0,1,2,3,5,6,7,8,9,10,11,12};
#define DEFAULT_THRES_TOP 8000
#define DEFAULT_THRES_BOT 12000

#elif defined(CONFIG_BADGE23_HW_GEN_P1)
static const uint8_t top_map[] = {2, 2, 2, 0, 0, 8, 8, 8, 6, 6, 4, 4};
static const uint8_t top_stages = 12;
static const uint8_t bot_map[] = {1, 1, 3, 3, 5, 5, 7, 7, 9, 9};
static const uint8_t bot_stages = 10;
static const uint8_t top_segment_map[] = {1,2,0,1,2,1,2,0,1,2,1,2}; //idk
static const uint8_t bot_segment_map[] = {3,0,3,0,3,0,0,3,0,3}; //idk
static const uint8_t bot_stage_config[] = {0,1,2,3,4,5,6,7,8,9,10,11};
#define DEFAULT_THRES_TOP 2000
#define DEFAULT_THRES_BOT 12000

#else
#error "captouch not implemented for this badge generation"
#endif

#if defined(CONFIG_BADGE23_HW_GEN_P4)
static const uint8_t top_segment_map[] = {1,3,2,2,3,1,1,3,2,1,3,2}; //PETAL_PAD_*
static const uint8_t bot_segment_map[] = {3,0,3,0,0,0,3,0,3,1,2,3}; //PETAL_PAD_*
#elif defined(CONFIG_BADGE23_HW_GEN_P6)
static const uint8_t top_segment_map[] = {1,3,2,2,3,1,1,3,2,1,3,2}; //PETAL_PAD_*
static const uint8_t bot_segment_map[] = {3,0,3,0,0,0,3,0,3,1,2,3}; //PETAL_PAD_*
#elif defined(CONFIG_BADGE23_HW_GEN_P3)
static const uint8_t top_segment_map[] = {0,1,2, 2,1,0, 0,1,2, 2,1,0}; //PETAL_PAD_*
static const uint8_t bot_segment_map[] = {3,0,3,0,0,0,3,0,3, 0,2,1}; //PETAL_PAD_*
#endif

static const char *TAG = "captouch";


#define AD7147_REG_PWR_CONTROL              0x00
#define AD7147_REG_STAGE_CAL_EN             0x01
#define AD7147_REG_STAGE_HIGH_INT_ENABLE    0x06
#define AD7147_REG_DEVICE_ID                0x17

#define TIMEOUT_MS                  1000

#define PETAL_PRESSED_DEBOUNCE 2

static struct ad714x_chip *chip_top;
static struct ad714x_chip *chip_bot;
typedef struct{
    uint8_t config_mask;
    uint16_t amb_values[4]; //ordered according to PETAL_PAD_*
    uint16_t cdc_values[4]; //ordered according to PETAL_PAD_*
    uint16_t thres_values[4]; //ordered according to PETAL_PAD_*
    uint8_t pressed;
} petal_t;

static petal_t petals[10];

struct ad714x_chip {
    flow3r_i2c_address *addr;
    uint8_t gpio;
    int pos_afe_offsets[13];
    int neg_afe_offsets[13];
    int neg_afe_offset_swap;
    int stages;
};

static struct ad714x_chip chip_top_rev5 = {
    .addr = &flow3r_i2c_addresses.touch_top,
    .gpio = 15,
    .pos_afe_offsets = {4, 2, 2, 2, 2, 3, 4, 2, 2, 2, 2, 0},
    .stages = top_stages,
};

static struct ad714x_chip chip_bot_rev5 = {
    .addr = &flow3r_i2c_addresses.touch_bottom,
    .gpio = 15,
    .pos_afe_offsets = {3, 2, 1, 1 ,1, 1, 1, 1, 2, 3, 3, 3},
    .stages = bot_stages,
};

static esp_err_t ad714x_i2c_write(const struct ad714x_chip *chip, const uint16_t reg, const uint16_t data)
{
    const uint8_t tx[] = {reg >> 8, reg & 0xFF, data >> 8, data & 0xFF};
    ESP_LOGD(TAG, "AD7147 write reg %X-> %X", reg, data);
    return flow3r_bsp_i2c_write_to_device(*chip->addr, tx, sizeof(tx), TIMEOUT_MS / portTICK_PERIOD_MS);
}

static esp_err_t ad714x_i2c_read(const struct ad714x_chip *chip, const uint16_t reg, uint16_t *data, const size_t len)
{
    const uint8_t tx[] = {reg >> 8, reg & 0xFF};
    uint8_t rx[len * 2];
    esp_err_t ret = flow3r_bsp_i2c_write_read_device(*chip->addr, tx, sizeof(tx), rx, sizeof(rx), TIMEOUT_MS / portTICK_PERIOD_MS);
    for(int i = 0; i < len; i++) {
        data[i] = (rx[i * 2] << 8) | rx[i * 2 + 1];
    }
    return ret;
}

struct ad7147_stage_config {
    unsigned int cinX_connection_setup[13];
    unsigned int se_connection_setup:2;
    unsigned int neg_afe_offset_disable:1;
    unsigned int pos_afe_offset_disable:1;
    unsigned int neg_afe_offset:6;
    unsigned int neg_afe_offset_swap:1;
    unsigned int pos_afe_offset:6;
    unsigned int pos_afe_offset_swap:1;
    unsigned int neg_threshold_sensitivity:4;
    unsigned int neg_peak_detect:3;
    unsigned int pos_threshold_sensitivity:4;
    unsigned int pos_peak_detect:3;
};


static const uint16_t bank2 = 0x80;

static void ad714x_set_stage_config(const struct ad714x_chip *chip, const uint8_t stage, const struct ad7147_stage_config * config)
{
    const uint16_t connection_6_0 = (config->cinX_connection_setup[6] << 12) | (config->cinX_connection_setup[5] << 10) | (config->cinX_connection_setup[4] << 8) | (config->cinX_connection_setup[3] << 6) | (config->cinX_connection_setup[2] << 4) | (config->cinX_connection_setup[1] << 2) | (config->cinX_connection_setup[0] << 0);
    const uint16_t connection_12_7 = (config->pos_afe_offset_disable << 15) | (config->neg_afe_offset_disable << 14) | (config->se_connection_setup << 12) | (config->cinX_connection_setup[12] << 10) | (config->cinX_connection_setup[11] << 8) | (config->cinX_connection_setup[10] << 6) | (config->cinX_connection_setup[9] << 4) | (config->cinX_connection_setup[8] << 2) | (config->cinX_connection_setup[7] << 0);
    const uint16_t afe_offset = (config->pos_afe_offset_swap << 15) | (config->pos_afe_offset << 8) | (config->neg_afe_offset_swap << 7) | (config->neg_afe_offset << 0);
    const uint16_t sensitivity = (config->pos_peak_detect << 12) | (config->pos_threshold_sensitivity << 8) | (config->neg_peak_detect << 4) | (config->neg_threshold_sensitivity << 0);

    //ESP_LOGI(TAG, "Stage %d config-> %X %X %X %X", stage, connection_6_0, connection_12_7, afe_offset, sensitivity);
    //ESP_LOGI(TAG, "Config: %X %X %X %X %X %X %X %X %X", config->pos_afe_offset_disable, config->pos_afe_offset_disable, config->se_connection_setup, config->cinX_connection_setup[12], config->cinX_connection_setup[11], config->cinX_connection_setup[10], config->cinX_connection_setup[9], config->cinX_connection_setup[8], config->cinX_connection_setup[7]);

    ad714x_i2c_write(chip, bank2 + stage * 8, connection_6_0);
    ad714x_i2c_write(chip, bank2 + stage * 8 + 1, connection_12_7);
    ad714x_i2c_write(chip, bank2 + stage * 8 + 2, afe_offset);
    ad714x_i2c_write(chip, bank2 + stage * 8 + 3, sensitivity);
}

struct ad7147_device_config {
    unsigned int power_mode:2;
    unsigned int lp_conv_delay:2;
    unsigned int sequence_stage_num:4;
    unsigned int decimation:2;
    unsigned int sw_reset:1;
    unsigned int int_pol:1;
    unsigned int ext_source:1;
    unsigned int cdc_bias:2;

    unsigned int stage0_cal_en:1;
    unsigned int stage1_cal_en:1;
    unsigned int stage2_cal_en:1;
    unsigned int stage3_cal_en:1;
    unsigned int stage4_cal_en:1;
    unsigned int stage5_cal_en:1;
    unsigned int stage6_cal_en:1;
    unsigned int stage7_cal_en:1;
    unsigned int stage8_cal_en:1;
    unsigned int stage9_cal_en:1;
    unsigned int stage10_cal_en:1;
    unsigned int stage11_cal_en:1;
    unsigned int avg_fp_skip:2;
    unsigned int avg_lp_skip:2;

    unsigned int stage0_high_int_enable:1;
    unsigned int stage1_high_int_enable:1;
    unsigned int stage2_high_int_enable:1;
    unsigned int stage3_high_int_enable:1;
    unsigned int stage4_high_int_enable:1;
    unsigned int stage5_high_int_enable:1;
    unsigned int stage6_high_int_enable:1;
    unsigned int stage7_high_int_enable:1;
    unsigned int stage8_high_int_enable:1;
    unsigned int stage9_high_int_enable:1;
    unsigned int stage10_high_int_enable:1;
    unsigned int stage11_high_int_enable:1;
};


static void ad714x_set_device_config(const struct ad714x_chip *chip, const struct ad7147_device_config * config)
{
    const uint16_t pwr_control = (config->cdc_bias << 14) | (config->ext_source << 12) | (config->int_pol << 11) | (config->sw_reset << 10) | (config->decimation << 8) | (config->sequence_stage_num << 4) | (config->lp_conv_delay << 2) | (config->power_mode << 0);
    const uint16_t stage_cal_en = (config->avg_lp_skip << 14) | (config->avg_fp_skip << 12) | (config->stage11_cal_en << 11) | (config->stage10_cal_en << 10) | (config->stage9_cal_en << 9) | (config->stage8_cal_en << 8) | (config->stage7_cal_en << 7) | (config->stage6_cal_en << 6) | (config->stage5_cal_en << 5) | (config->stage4_cal_en << 4) | (config->stage3_cal_en << 3) | (config->stage2_cal_en << 2) | (config->stage1_cal_en << 1) | (config->stage0_cal_en << 0);
    const uint16_t stage_high_int_enable = (config->stage11_high_int_enable << 11) | (config->stage10_high_int_enable << 10) | (config->stage9_high_int_enable << 9) | (config->stage8_high_int_enable << 8) | (config->stage7_high_int_enable << 7) | (config->stage6_high_int_enable << 6) | (config->stage5_high_int_enable << 5) | (config->stage4_high_int_enable << 4) | (config->stage3_high_int_enable << 3) | (config->stage2_high_int_enable << 2) | (config->stage1_high_int_enable << 1) | (config->stage0_high_int_enable << 0);

    ad714x_i2c_write(chip, AD7147_REG_PWR_CONTROL, pwr_control);
    ad714x_i2c_write(chip, AD7147_REG_STAGE_CAL_EN, stage_cal_en);
    ad714x_i2c_write(chip, AD7147_REG_STAGE_HIGH_INT_ENABLE, stage_high_int_enable);
}

static struct ad7147_stage_config ad714x_default_config(void)
{
    return (struct ad7147_stage_config) {
            .cinX_connection_setup={CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS, CIN_BIAS},
            .se_connection_setup=0b01,
            .pos_afe_offset=0,
        };
}

static void captouch_configure_stage(struct ad714x_chip * chip, uint8_t stage){
    struct ad7147_stage_config stage_config;
    stage_config = ad714x_default_config();
    if(chip == chip_bot){
        stage_config.cinX_connection_setup[bot_stage_config[stage]] = CIN_CDC_POS;
    } else {
        stage_config.cinX_connection_setup[stage] = CIN_CDC_POS;
    }
    stage_config.pos_afe_offset=chip->pos_afe_offsets[stage];
    ad714x_set_stage_config(chip, stage, &stage_config);
}

static int8_t captouch_configure_stage_afe_offset(uint8_t top, uint8_t stage, int8_t delta_afe){
    int8_t sat = 0;
    struct ad714x_chip * chip = chip_bot;
    if(top) chip = chip_top;
    int8_t afe = chip->pos_afe_offsets[stage] - chip->neg_afe_offsets[stage];
    if((afe >= 63) && (delta_afe > 0)) sat = 1;
    if((afe <= 63) && (delta_afe < 0)) sat = -1;
    afe += delta_afe;
    if(afe >= 63) afe = 63;
    if(afe <= -63)afe = -63;

    if(afe>0){
        chip->pos_afe_offsets[stage] = afe;
        chip->neg_afe_offsets[stage] = 0;
    } else {
        chip->pos_afe_offsets[stage] = 0;
        chip->neg_afe_offsets[stage] = -afe;
    }
    captouch_configure_stage(chip, stage);
    return sat;
}

static void captouch_init_chip(struct ad714x_chip* chip, const struct ad7147_device_config device_config)
{
    uint16_t data;
    ad714x_i2c_read(chip, AD7147_REG_DEVICE_ID, &data, 1);
    ESP_LOGI(TAG, "DEVICE ID = %X", data);

    ad714x_set_device_config(chip, &device_config);

    for(int i=0; i<chip->stages; i++) {
        captouch_configure_stage(chip, i);
    }
}

static void captouch_init_petals(){
    for(int i = 0; i < 10; i++){
        for(int j = 0; j < 4; j++){
            petals[i].amb_values[j] = 0;
            petals[i].cdc_values[j] = 0;
            if(i%2){
                petals[i].thres_values[j] = DEFAULT_THRES_BOT;
            } else {
                petals[i].thres_values[j] = DEFAULT_THRES_TOP;
            }
        }
        petals[i].config_mask = 0;
    }
    for(int i = 0; i < bot_stages; i++){
        petals[bot_map[i]].config_mask |= 1 << bot_segment_map[i]; 
    }
    for(int i = 0; i < top_stages; i++){
        petals[top_map[i]].config_mask |= 1 << top_segment_map[i]; 
    }
}

int32_t captouch_get_petal_rad(uint8_t petal){
    if(petal > 9) petal = 9;
    uint8_t cf = petals[petal].config_mask;
    #if defined(CONFIG_BADGE23_TOP_BOARD_SPIKES)
    if(cf == 0b1110){ //CCW, CW, BASE
        int32_t left = petals[petal].cdc_values[PETAL_PAD_CCW];
        left -= petals[petal].amb_values[PETAL_PAD_CCW];
        int32_t right = petals[petal].cdc_values[PETAL_PAD_CW];
        right -= petals[petal].amb_values[PETAL_PAD_CW];
        int32_t base = petals[petal].cdc_values[PETAL_PAD_BASE];
        base -= petals[petal].amb_values[PETAL_PAD_BASE];
        return (left + right)/2 - base;
    }
    #elif defined(CONFIG_BADGE23_TOP_BOARD_SPIRALS)
    if(cf == 0b1110){ //CCW, CW, BASE
        int32_t left = petals[petal].cdc_values[PETAL_PAD_CCW];
        left -= petals[petal].amb_values[PETAL_PAD_CCW];
        int32_t right = petals[petal].cdc_values[PETAL_PAD_CW];
        right -= petals[petal].amb_values[PETAL_PAD_CW];
        int32_t base = petals[petal].cdc_values[PETAL_PAD_BASE];
        base -= petals[petal].amb_values[PETAL_PAD_BASE];
        return (left + right)/2 - base;
    }
    #endif
    if(cf == 0b111){ //CCW, CW, TIP
        int32_t left = petals[petal].cdc_values[PETAL_PAD_CCW];
        left -= petals[petal].amb_values[PETAL_PAD_CCW];
        int32_t right = petals[petal].cdc_values[PETAL_PAD_CW];
        right -= petals[petal].amb_values[PETAL_PAD_CW];
        int32_t tip = petals[petal].cdc_values[PETAL_PAD_TIP];
        tip -= petals[petal].amb_values[PETAL_PAD_TIP];
        return (-left - right)/2 + tip;
    }
    if(cf == 0b1001){ //TIP, BASE
        int32_t tip = petals[petal].cdc_values[PETAL_PAD_TIP];
        tip -= petals[petal].amb_values[PETAL_PAD_TIP];
        int32_t base = petals[petal].cdc_values[PETAL_PAD_BASE];
        base -= petals[petal].amb_values[PETAL_PAD_BASE];
        return tip - base;
    }
    if(cf == 0b1){ //TIP
        int32_t tip = petals[petal].cdc_values[PETAL_PAD_TIP];
        tip -= petals[petal].amb_values[PETAL_PAD_TIP];
        return tip;
    }
    return 0;
}

int32_t captouch_get_petal_phi(uint8_t petal){
    if(petal > 9) petal = 9;
    uint8_t cf = petals[petal].config_mask;
    #if defined(CONFIG_BADGE23_TOP_BOARD_SPIKES)
    if((cf == 0b1110) || (cf == 0b110) || (cf == 0b111)){ //CCW, CW, (BASE)
        int32_t left = petals[petal].cdc_values[PETAL_PAD_CCW];
        left -= petals[petal].amb_values[PETAL_PAD_CCW];
        int32_t right = petals[petal].cdc_values[PETAL_PAD_CW];
        right -= petals[petal].amb_values[PETAL_PAD_CW];
        return left - right;
    }
    #elif defined(CONFIG_BADGE23_TOP_BOARD_SPIRALS)
    if((cf == 0b1110) || (cf == 0b110) || (cf == 0b111)){ //CCW, CW, (BASE)
        int32_t left = petals[petal].cdc_values[PETAL_PAD_CCW];
        left -= petals[petal].amb_values[PETAL_PAD_CCW];
        int32_t right = petals[petal].cdc_values[PETAL_PAD_CW];
        right -= petals[petal].amb_values[PETAL_PAD_CW];
        return left - right;
    }
    #endif
    return 0;
}

void captouch_init(void)
{
    captouch_init_petals();
    chip_top = &chip_top_rev5;
    chip_bot = &chip_bot_rev5;

    captouch_init_chip(chip_top, (struct ad7147_device_config){.sequence_stage_num = 11,
                                                 .decimation = 1,
                                                 });

    captouch_init_chip(chip_bot, (struct ad7147_device_config){.sequence_stage_num = 11,
                                                 .decimation = 1,
                                                 });
}

uint16_t read_captouch(){
    uint16_t bin_petals = 0;
    for(int i = 0; i < 10; i++) {
        if(petals[i].pressed){
            bin_petals |= (1<<i);
        }
    }
    return bin_petals;
}

uint16_t cdc_data[2][12] = {0,};
uint16_t cdc_ambient[2][12] = {0,};

static volatile uint32_t calib_active = 0;

static uint8_t calib_cycles = 0;
void captouch_force_calibration(){
    if(!calib_cycles){ //last calib has finished
        calib_cycles = 16; //goal cycles, can be argument someday
        Atomic_Increment_u32(&calib_active);
    }
}

uint8_t captouch_calibration_active(){
    return Atomic_CompareAndSwap_u32(&calib_active, 0, 0) == ATOMIC_COMPARE_AND_SWAP_FAILURE;
}


void check_petals_pressed(){
    for(int i = 0; i < 10; i++){
        bool pressed = 0;
        bool prev = petals[i].pressed;
        for(int j = 0; j < 4; j++){
            if((petals[i].amb_values[j] +
                petals[i].thres_values[j]) <
                petals[i].cdc_values[j]){
                pressed = 1;
            }
        }
        if(pressed){
            petals[i].pressed = PETAL_PRESSED_DEBOUNCE;
        } else if(petals[i].pressed){
            petals[i].pressed--;
        }

        if(petals[i].pressed && (!prev)){
            // TODO: PETAL_PRESS_CALLBACK
        }
        if((!petals[i].pressed) && prev){
            // TODO: PETAL_RELEASE_CALLBACK
        }
    }
}

void cdc_to_petal(bool bot, bool amb, uint16_t cdc_data[], uint8_t cdc_data_length){
    if(!bot){
        for(int i = 0; i < cdc_data_length; i++){
            if(amb){
                petals[top_map[i]].amb_values[top_segment_map[i]] = cdc_data[i];
            } else {
                petals[top_map[i]].cdc_values[top_segment_map[i]] = cdc_data[i];
            }
        }
    } else {
        for(int i = 0; i < cdc_data_length; i++){
            if(amb){
                petals[bot_map[i]].amb_values[bot_segment_map[i]] = cdc_data[i];
            } else {
                petals[bot_map[i]].cdc_values[bot_segment_map[i]] = cdc_data[i];
            }
        }
    }
}

uint16_t captouch_get_petal_pad_raw(uint8_t petal, uint8_t pad){
    if(petal > 9) petal = 9;
    if(pad > 3) pad = 3;
    return petals[petal].cdc_values[pad];
}
uint16_t captouch_get_petal_pad_calib_ref(uint8_t petal, uint8_t pad){
    if(petal > 9) petal = 9;
    if(pad > 3) pad = 3;
    return petals[petal].amb_values[pad];
}
uint16_t captouch_get_petal_pad(uint8_t petal, uint8_t pad){
    if(petal > 9) petal = 9;
    if(pad > 3) pad = 3;
    if(petals[petal].amb_values[pad] < petals[petal].cdc_values[pad]){
        return petals[petal].cdc_values[pad] - petals[petal].amb_values[pad];
    }
    return 0;
}

void captouch_set_petal_pad_threshold(uint8_t petal, uint8_t pad, uint16_t thres){
    if(petal > 9) petal = 9;
    if(pad > 3) pad = 3;
    petals[petal].thres_values[pad] = thres;    
}

static int32_t calib_target = 6000;

void captouch_set_calibration_afe_target(uint16_t target){
    calib_target = target;
}

void captouch_read_cycle(){
        static uint8_t calib_cycle = 0; 
        static uint8_t calib_div = 1;
        static uint32_t ambient_acc[2][12] = {{0,}, {0,}};
        if(calib_cycles){
            if(calib_cycle == 0){ // last cycle has finished, setup new
                calib_cycle = calib_cycles;
                calib_div = calib_cycles;
                for(int j=0;j<12;j++){
                    ambient_acc[0][j] = 0;
                    ambient_acc[1][j] = 0;
                }
            }

            ad714x_i2c_read(chip_top, 0xB, cdc_ambient[0], chip_top->stages);
            ad714x_i2c_read(chip_bot, 0xB, cdc_ambient[1], chip_bot->stages);
            for(int j=0;j<12;j++){
                ambient_acc[0][j] += cdc_ambient[0][j];
                ambient_acc[1][j] += cdc_ambient[1][j];
            }

            // TODO: use median instead of average
            calib_cycle--;
            if(!calib_cycle){ //calib cycle is complete
                for(int i = 0; i < 12; i++){
                    cdc_ambient[0][i] = ambient_acc[0][i] / calib_div;
                    cdc_ambient[1][i] = ambient_acc[1][i] / calib_div;
                }
                cdc_to_petal(0, 1, cdc_ambient[0], 12);
                cdc_to_petal(1, 1, cdc_ambient[1], 12);
                calib_cycles = 0;
                
                uint8_t recalib = 0;
                for(int i = 0; i < 12; i++){
                    for(int j = 0; j < 2; j++){
                        int32_t diff = ((int32_t) cdc_ambient[j][i]) - calib_target;
                        int8_t steps = diff/(AFE_INCR_CAP);
                        if((steps > 1) || (steps < -1)){
                            if(!captouch_configure_stage_afe_offset(1-j, i, steps)){
                                recalib = 1;
                            }
                        }
                    }
                }
                if(recalib){
                    calib_cycles = 16; // do another round
                } else {
                    Atomic_Decrement_u32(&calib_active);
                }
            }
        } else {
            ad714x_i2c_read(chip_top, 0xB, cdc_data[0], chip_top->stages);
            cdc_to_petal(0, 0, cdc_data[0], 12);

            ad714x_i2c_read(chip_bot, 0xB, cdc_data[1], chip_bot->stages);
            cdc_to_petal(1, 0, cdc_data[1], 12);

            check_petals_pressed();
        }
}

static void captouch_print_debug_info_chip(const struct ad714x_chip* chip)
{
    uint16_t *data;
    uint16_t *ambient;
    const int stages = chip->stages;

    if(chip == chip_top) {
        data = cdc_data[0];
        ambient = cdc_ambient[0];
    } else {
        data = cdc_data[1];
        ambient = cdc_ambient[1];
    }

    ESP_LOGI(TAG, "CDC results: %X %X %X %X %X %X %X %X %X %X %X %X", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11]);

    for(int stage=0; stage<stages; stage++) {
        ESP_LOGI(TAG, "stage %d ambient: %X diff: %d", stage, ambient[stage], data[stage] - ambient[stage]);
    }
}

void captouch_print_debug_info(void)
{
    captouch_print_debug_info_chip(chip_top);
    captouch_print_debug_info_chip(chip_bot);
}