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