/*
 * 自然写智能点阵笔嵌入式固件软件 V1.0
 * pressure_sensor.c - 压力传感器ADC驱动
 *
 * 功能说明：
 * 1. 笔尖压力传感器ADC采样
 * 2. 传感器零点校准与温度补偿
 * 3. 压力值滤波与去抖
 * 4. 压力触发阈值检测
 */

#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include "hal_adc.h"
#include "hal_i2c.h"

/* ========== 常量定义 ========== */

/* ADC通道号（压力传感器） */
#define PRESSURE_ADC_CHANNEL    1

/* ADC分辨率 */
#define ADC_RESOLUTION          4095

/* 校准样本数量 */
#define CALIBRATION_SAMPLES     32

/* 压力触发阈值（原始ADC值，高于此值认为笔尖接触） */
#define PRESSURE_TRIGGER_THRESHOLD  100

/* IIR低通滤波系数（0.0~1.0，越小滤波越强） */
#define PRESSURE_FILTER_ALPHA   0.3f

/* 温度传感器I2C地址 */
#define TEMP_SENSOR_I2C_ADDR    0x48

/* ========== 静态变量 ========== */

/* 零点偏移（校准时测量的无负荷值） */
static uint16_t s_zero_offset = 0;

/* 温度补偿系数 */
static float s_temp_coefficient = 0.0f;

/* 滤波后的压力值 */
static float s_filtered_pressure = 0.0f;

/* 是否已校准 */
static bool s_calibrated = false;

/* 当前温度（摄氏度） */
static float s_current_temp = 25.0f;

/* ========== 初始化 ========== */

/**
 * 初始化压力传感器
 * 配置ADC通道，设置采样参数
 */
void pressure_sensor_init(void) {
    /* 配置ADC通道 */
    hal_adc_init(PRESSURE_ADC_CHANNEL, 12);  /* 12位分辨率 */

    /* 设置采样时间（较长的采样时间提高精度） */
    hal_adc_set_sample_time(PRESSURE_ADC_CHANNEL, 84);  /* 84个时钟周期 */

    s_filtered_pressure = 0;
    s_calibrated = false;
}

/* ========== 零点校准 ========== */

/**
 * 执行零点校准
 * 在笔尖无负荷状态下，多次采样取平均作为零点偏移
 * 应在每次开机时或温度变化较大时调用
 *
 * @return 0成功, -1采样异常
 */
int pressure_sensor_calibrate(void) {
    uint32_t sum = 0;
    uint16_t min_val = ADC_RESOLUTION;
    uint16_t max_val = 0;

    /* 采集多个样本 */
    int i;
    for (i = 0; i < CALIBRATION_SAMPLES; i++) {
        uint16_t sample = hal_adc_read(PRESSURE_ADC_CHANNEL);
        sum += sample;

        if (sample < min_val) min_val = sample;
        if (sample > max_val) max_val = sample;

        /* 简单延时等待ADC稳定 */
        for (volatile int d = 0; d < 1000; d++);
    }

    /* 检查采样一致性（极差不应太大） */
    if ((max_val - min_val) > 50) {
        /* 采样波动太大，可能笔尖正在受力 */
        return -1;
    }

    /* 去掉最大最小值后求平均 */
    sum = sum - min_val - max_val;
    s_zero_offset = (uint16_t)(sum / (CALIBRATION_SAMPLES - 2));

    s_calibrated = true;
    return 0;
}

/* ========== 温度补偿 ========== */

/**
 * 读取温度传感器（I2C接口）
 * 用于压力值的温度漂移补偿
 *
 * @return 温度值（摄氏度），读取失败返回25.0
 */
static float read_temperature(void) {
    uint8_t temp_data[2];
    int ret = hal_i2c_read(I2C_PORT_1, TEMP_SENSOR_I2C_ADDR,
                            0x00, temp_data, 2);

    if (ret != 0) {
        return 25.0f;  /* 读取失败，使用默认温度 */
    }

    /* 解析12位温度值（LM75格式） */
    int16_t raw_temp = ((int16_t)temp_data[0] << 4) | (temp_data[1] >> 4);
    if (raw_temp & 0x0800) {
        raw_temp |= 0xF000;  /* 符号扩展 */
    }

    return (float)raw_temp * 0.0625f;
}

/**
 * 计算温度补偿后的压力值
 * 压力传感器的输出会随温度漂移
 * 补偿公式：P_comp = P_raw - offset - k_temp * (T - T_ref)
 *
 * @param raw_value  原始ADC值
 * @return 补偿后的值
 */
static uint16_t apply_temperature_compensation(uint16_t raw_value) {
    /* 参考温度（校准时的温度） */
    const float t_ref = 25.0f;

    /* 温度补偿偏移量 */
    float temp_offset = s_temp_coefficient * (s_current_temp - t_ref);

    int32_t compensated = (int32_t)raw_value - (int32_t)s_zero_offset
                          - (int32_t)temp_offset;

    if (compensated < 0) compensated = 0;
    if (compensated > ADC_RESOLUTION) compensated = ADC_RESOLUTION;

    return (uint16_t)compensated;
}

/* ========== 压力读取接口 ========== */

/**
 * 读取原始压力ADC值
 * @return 原始12位ADC值（0-4095）
 */
uint16_t pressure_sensor_read_raw(void) {
    return hal_adc_read(PRESSURE_ADC_CHANNEL);
}

/**
 * 读取处理后的压力值
 * 包含零点校准、温度补偿和低通滤波
 *
 * @return 处理后的压力值（0-4095）
 */
uint16_t pressure_sensor_read(void) {
    /* 读取原始ADC值 */
    uint16_t raw = hal_adc_read(PRESSURE_ADC_CHANNEL);

    /* 温度补偿（每100次读取更新一次温度） */
    static uint16_t temp_update_count = 0;
    if (++temp_update_count >= 100) {
        temp_update_count = 0;
        s_current_temp = read_temperature();
    }

    /* 应用温度补偿和零点校准 */
    uint16_t compensated = apply_temperature_compensation(raw);

    /* IIR低通滤波 */
    s_filtered_pressure = PRESSURE_FILTER_ALPHA * (float)compensated
                         + (1.0f - PRESSURE_FILTER_ALPHA) * s_filtered_pressure;

    return (uint16_t)s_filtered_pressure;
}

/**
 * 检测笔尖是否接触纸面（基于压力阈值）
 * @return true=接触, false=悬空
 */
bool pressure_sensor_is_touching(void) {
    uint16_t raw = hal_adc_read(PRESSURE_ADC_CHANNEL);
    int32_t adjusted = (int32_t)raw - (int32_t)s_zero_offset;

    return (adjusted > PRESSURE_TRIGGER_THRESHOLD);
}

/**
 * 获取校准状态
 */
bool pressure_sensor_is_calibrated(void) {
    return s_calibrated;
}

/**
 * 设置温度补偿系数
 * 可通过实验测量不同温度下的零点漂移来确定
 *
 * @param coefficient  补偿系数（ADC单位/摄氏度）
 */
void pressure_sensor_set_temp_coeff(float coefficient) {
    s_temp_coefficient = coefficient;
}
