software copyright
This commit is contained in:
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* 自然写智能点阵笔嵌入式固件软件 V1.0
|
||||
* camera_driver.c - CMOS摄像头传感器驱动
|
||||
*
|
||||
* 功能说明:
|
||||
* 1. CMOS图像传感器SPI通信驱动
|
||||
* 2. 传感器寄存器配置(曝光、增益、帧率)
|
||||
* 3. 图像采集触发与数据读取
|
||||
* 4. 传感器电源管理(开/关/低功耗)
|
||||
* 5. 自检与故障检测
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hal_spi.h"
|
||||
#include "hal_gpio.h"
|
||||
|
||||
/* ========== 传感器寄存器地址 ========== */
|
||||
|
||||
/* 芯片ID寄存器(只读) */
|
||||
#define REG_CHIP_ID 0x00
|
||||
|
||||
/* 系统控制寄存器 */
|
||||
#define REG_SYS_CTRL 0x01
|
||||
#define SYS_CTRL_RESET 0x80 /* 软复位 */
|
||||
#define SYS_CTRL_SLEEP 0x40 /* 睡眠模式 */
|
||||
#define SYS_CTRL_ENABLE 0x01 /* 使能采集 */
|
||||
|
||||
/* 曝光时间寄存器(高/低字节) */
|
||||
#define REG_EXPOSURE_H 0x02
|
||||
#define REG_EXPOSURE_L 0x03
|
||||
|
||||
/* 模拟增益寄存器 */
|
||||
#define REG_GAIN 0x04
|
||||
|
||||
/* 帧率控制寄存器 */
|
||||
#define REG_FRAME_RATE 0x05
|
||||
|
||||
/* 像素数据起始寄存器(读取时自动递增) */
|
||||
#define REG_PIXEL_DATA 0x10
|
||||
|
||||
/* 帧就绪状态位 */
|
||||
#define REG_STATUS 0x0F
|
||||
#define STATUS_FRAME_READY 0x01
|
||||
|
||||
/* 预期芯片ID值 */
|
||||
#define EXPECTED_CHIP_ID 0xA5
|
||||
|
||||
/* ========== 传感器模式枚举 ========== */
|
||||
|
||||
#define CAMERA_MODE_SINGLE 0 /* 单帧模式 */
|
||||
#define CAMERA_MODE_CONTINUOUS 1 /* 连续帧模式 */
|
||||
|
||||
/* ========== GPIO引脚定义 ========== */
|
||||
|
||||
#define GPIO_CAMERA_POWER 12 /* 传感器电源控制引脚 */
|
||||
#define GPIO_CAMERA_CS 15 /* SPI片选引脚 */
|
||||
#define GPIO_CAMERA_LED 16 /* 红外LED照明引脚 */
|
||||
|
||||
/* ========== SPI通信 ========== */
|
||||
|
||||
/* SPI端口号 */
|
||||
#define CAMERA_SPI_PORT SPI_PORT_1
|
||||
|
||||
/* 读寄存器标志位 */
|
||||
#define SPI_READ_FLAG 0x80
|
||||
|
||||
/* ========== 静态变量 ========== */
|
||||
|
||||
/* 传感器是否已初始化 */
|
||||
static bool s_camera_initialized = false;
|
||||
|
||||
/* 传感器是否已上电 */
|
||||
static bool s_camera_powered = false;
|
||||
|
||||
/* 当前工作模式 */
|
||||
static uint8_t s_camera_mode = CAMERA_MODE_SINGLE;
|
||||
|
||||
/* ========== SPI底层读写 ========== */
|
||||
|
||||
/**
|
||||
* SPI写单个寄存器
|
||||
* @param reg_addr 寄存器地址(7位)
|
||||
* @param value 写入值
|
||||
*/
|
||||
static void camera_write_reg(uint8_t reg_addr, uint8_t value) {
|
||||
uint8_t tx[2];
|
||||
tx[0] = reg_addr & 0x7F; /* 最高位0=写操作 */
|
||||
tx[1] = value;
|
||||
|
||||
hal_gpio_write(GPIO_CAMERA_CS, 0); /* 拉低CS */
|
||||
hal_spi_transfer(CAMERA_SPI_PORT, tx, NULL, 2);
|
||||
hal_gpio_write(GPIO_CAMERA_CS, 1); /* 拉高CS */
|
||||
}
|
||||
|
||||
/**
|
||||
* SPI读单个寄存器
|
||||
* @param reg_addr 寄存器地址
|
||||
* @return 读取的值
|
||||
*/
|
||||
static uint8_t camera_read_reg(uint8_t reg_addr) {
|
||||
uint8_t tx[2], rx[2];
|
||||
tx[0] = reg_addr | SPI_READ_FLAG; /* 最高位1=读操作 */
|
||||
tx[1] = 0x00; /* 空字节用于接收数据 */
|
||||
|
||||
hal_gpio_write(GPIO_CAMERA_CS, 0);
|
||||
hal_spi_transfer(CAMERA_SPI_PORT, tx, rx, 2);
|
||||
hal_gpio_write(GPIO_CAMERA_CS, 1);
|
||||
|
||||
return rx[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* SPI批量读取像素数据
|
||||
* 使用DMA方式高速读取整帧图像数据
|
||||
*
|
||||
* @param buffer 接收缓冲区
|
||||
* @param length 读取字节数
|
||||
*/
|
||||
static void camera_read_pixels(uint8_t *buffer, uint16_t length) {
|
||||
uint8_t cmd = REG_PIXEL_DATA | SPI_READ_FLAG;
|
||||
|
||||
hal_gpio_write(GPIO_CAMERA_CS, 0);
|
||||
|
||||
/* 先发送寄存器地址 */
|
||||
hal_spi_transfer(CAMERA_SPI_PORT, &cmd, NULL, 1);
|
||||
|
||||
/* 然后连续读取像素数据 */
|
||||
hal_spi_receive(CAMERA_SPI_PORT, buffer, length);
|
||||
|
||||
hal_gpio_write(GPIO_CAMERA_CS, 1);
|
||||
}
|
||||
|
||||
/* ========== 传感器初始化 ========== */
|
||||
|
||||
/**
|
||||
* 初始化CMOS图像传感器
|
||||
* 配置GPIO、验证芯片ID、设置初始参数
|
||||
*
|
||||
* @return 0成功, -1芯片ID错误, -2通信失败
|
||||
*/
|
||||
int camera_driver_init(void) {
|
||||
/* 配置控制GPIO为输出 */
|
||||
hal_gpio_config_output(GPIO_CAMERA_POWER);
|
||||
hal_gpio_config_output(GPIO_CAMERA_CS);
|
||||
hal_gpio_config_output(GPIO_CAMERA_LED);
|
||||
|
||||
/* CS默认高电平(不选中) */
|
||||
hal_gpio_write(GPIO_CAMERA_CS, 1);
|
||||
|
||||
/* 上电 */
|
||||
hal_gpio_write(GPIO_CAMERA_POWER, 1);
|
||||
s_camera_powered = true;
|
||||
|
||||
/* 等待传感器启动(典型10ms) */
|
||||
for (volatile int i = 0; i < 100000; i++);
|
||||
|
||||
/* 软复位 */
|
||||
camera_write_reg(REG_SYS_CTRL, SYS_CTRL_RESET);
|
||||
for (volatile int i = 0; i < 50000; i++);
|
||||
|
||||
/* 验证芯片ID */
|
||||
uint8_t chip_id = camera_read_reg(REG_CHIP_ID);
|
||||
if (chip_id != EXPECTED_CHIP_ID) {
|
||||
s_camera_initialized = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 设置默认参数 */
|
||||
camera_write_reg(REG_EXPOSURE_H, 0x00);
|
||||
camera_write_reg(REG_EXPOSURE_L, 0x80); /* 曝光值128 */
|
||||
camera_write_reg(REG_GAIN, 0x40); /* 增益64 */
|
||||
camera_write_reg(REG_FRAME_RATE, 100); /* 100Hz帧率 */
|
||||
|
||||
/* 使能传感器 */
|
||||
camera_write_reg(REG_SYS_CTRL, SYS_CTRL_ENABLE);
|
||||
|
||||
s_camera_initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ========== 参数配置 ========== */
|
||||
|
||||
/**
|
||||
* 设置曝光时间
|
||||
* @param exposure 曝光值(0-255,映射到传感器实际曝光时间)
|
||||
*/
|
||||
void camera_set_exposure(uint8_t exposure) {
|
||||
if (!s_camera_initialized) return;
|
||||
camera_write_reg(REG_EXPOSURE_H, 0x00);
|
||||
camera_write_reg(REG_EXPOSURE_L, exposure);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置模拟增益
|
||||
* @param gain 增益值(0-255)
|
||||
*/
|
||||
void camera_set_gain(uint8_t gain) {
|
||||
if (!s_camera_initialized) return;
|
||||
camera_write_reg(REG_GAIN, gain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置工作模式
|
||||
* @param mode CAMERA_MODE_SINGLE 或 CAMERA_MODE_CONTINUOUS
|
||||
*/
|
||||
void camera_set_mode(uint8_t mode) {
|
||||
s_camera_mode = mode;
|
||||
}
|
||||
|
||||
/* ========== 图像采集 ========== */
|
||||
|
||||
/**
|
||||
* 触发单帧采集
|
||||
* 在连续模式下,传感器会自动拍摄
|
||||
* 在单帧模式下,需要每次手动触发
|
||||
*/
|
||||
void camera_trigger_capture(void) {
|
||||
if (!s_camera_initialized || !s_camera_powered) return;
|
||||
|
||||
if (s_camera_mode == CAMERA_MODE_SINGLE) {
|
||||
/* 单帧模式:写触发位 */
|
||||
uint8_t ctrl = camera_read_reg(REG_SYS_CTRL);
|
||||
camera_write_reg(REG_SYS_CTRL, ctrl | 0x02);
|
||||
}
|
||||
|
||||
/* 开启红外LED照明(点阵图案需要红外光照射才能看到) */
|
||||
hal_gpio_write(GPIO_CAMERA_LED, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 等待帧就绪
|
||||
* @param timeout_ms 超时毫秒数
|
||||
* @return true帧已就绪, false超时
|
||||
*/
|
||||
bool camera_wait_frame_ready(uint16_t timeout_ms) {
|
||||
uint16_t elapsed = 0;
|
||||
while (elapsed < timeout_ms) {
|
||||
uint8_t status = camera_read_reg(REG_STATUS);
|
||||
if (status & STATUS_FRAME_READY) {
|
||||
return true;
|
||||
}
|
||||
/* 简单延时 */
|
||||
for (volatile int i = 0; i < 1000; i++);
|
||||
elapsed++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取传感器数据寄存器地址(用于DMA配置)
|
||||
*/
|
||||
uint32_t camera_get_data_register(void) {
|
||||
/* 返回SPI数据寄存器的内存映射地址 */
|
||||
return hal_spi_get_data_addr(CAMERA_SPI_PORT);
|
||||
}
|
||||
|
||||
/* ========== 电源管理 ========== */
|
||||
|
||||
/**
|
||||
* 传感器上电
|
||||
*/
|
||||
void camera_power_on(void) {
|
||||
if (s_camera_powered) return;
|
||||
|
||||
hal_gpio_write(GPIO_CAMERA_POWER, 1);
|
||||
s_camera_powered = true;
|
||||
|
||||
/* 等待传感器稳定 */
|
||||
for (volatile int i = 0; i < 100000; i++);
|
||||
|
||||
/* 重新使能 */
|
||||
camera_write_reg(REG_SYS_CTRL, SYS_CTRL_ENABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 传感器断电(最低功耗)
|
||||
*/
|
||||
void camera_power_off(void) {
|
||||
if (!s_camera_powered) return;
|
||||
|
||||
/* 关闭红外LED */
|
||||
hal_gpio_write(GPIO_CAMERA_LED, 0);
|
||||
|
||||
/* 传感器进入睡眠 */
|
||||
camera_write_reg(REG_SYS_CTRL, SYS_CTRL_SLEEP);
|
||||
|
||||
/* 切断电源 */
|
||||
hal_gpio_write(GPIO_CAMERA_POWER, 0);
|
||||
s_camera_powered = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 传感器自检
|
||||
* 检查SPI通信是否正常、芯片ID是否正确
|
||||
*
|
||||
* @return 0正常, -1通信故障, -2芯片ID异常
|
||||
*/
|
||||
int camera_self_test(void) {
|
||||
if (!s_camera_powered) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t chip_id = camera_read_reg(REG_CHIP_ID);
|
||||
if (chip_id != EXPECTED_CHIP_ID) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* 写读测试:写入一个可写寄存器并读回验证 */
|
||||
uint8_t test_val = 0x55;
|
||||
camera_write_reg(REG_GAIN, test_val);
|
||||
uint8_t read_back = camera_read_reg(REG_GAIN);
|
||||
|
||||
if (read_back != test_val) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 恢复原始增益值 */
|
||||
camera_write_reg(REG_GAIN, 0x40);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 自然写智能点阵笔嵌入式固件软件 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;
|
||||
}
|
||||
Reference in New Issue
Block a user