Files
system-design/software-copyright/12-writech-pen-firmware/driver/pressure_sensor.c
T
2026-03-22 15:24:40 +08:00

228 lines
5.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 自然写智能点阵笔嵌入式固件软件 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;
}