传感器驱动
本文档介绍 AW33N BLE SDK 中传感器驱动的整体框架与实现,涵盖重力传感器(G-Sensor / 加速度计)与光学鼠标传感器(Optical Mouse Sensor)两套驱动体系,包括注册机制、IIC 通信原语、数据读取流程与配置选项。
Purpose and Scope
本页面向 apps/app/bsp/common/gsensor 与 apps/app/bsp/common/mouse_sensor 两个传感器驱动子系统,说明:
- G-Sensor 驱动框架:
gsensor_api.c/h(应用层 API)、gSensor_manage.c/h(管理层)、具体芯片驱动(msa310.c、SC7A20_E.c、SC7A20_TR.c); - 光学鼠标传感器框架:
OMSensor_manage.c/h与 HAL3205 芯片驱动; - IIC 读写原语、寄存器命令枚举、自旋锁并发保护与传感器搜索机制;
- 相关编译开关(
TCFG_GSENSOR_ENABLE、TCFG_SC7A20_EN、TCFG_SC7A20_E_EN、TCFG_MSA310_EN等)与平台配置宏。
不属于本页范围的相邻主题:按键驱动(key)、LED 驱动(led)、红外编解码(ir)等外设驱动各有独立页面;FindMy 场景中通过 UART 透传传感器的示例(ble_fmy_sensor_uart.c)属于示例应用,本页仅作为扩展点提及。
Overview
在嵌入式 BLE 产品(如智能手环、防丢器、无线鼠标)中,传感器驱动需要同时满足三个诉求:芯片可替换性(不同供应商的加速度计寄存器各不相同)、总线资源独占(IIC 为多外设共享总线,需要互斥保护)、低功耗与运动检测(被动/主动两种检测状态)。
SDK 采用经典的三段式驱动分层 + 链接段注册表设计:
- 设备层(Device):每个芯片驱动(MSA310、SC7A20、HAL3205 等)通过宏
REGISTER_GRAVITY_SENSOR/REGISTER_OMSENSOR把自己注册进链接脚本的专用段(.gsensor_dev/.omsensor_dev),驱动代码无需修改框架; - 管理层(Manager):
gSensor_manage.c遍历注册段查找可用传感器,提供统一 IIC 读写原语(带自旋锁保护)、命令分发与平台数据解析; - 应用层(API):
gsensor_api.c对上层暴露sensor_init、sensor_data_get、get_sensor_avg、sensor_motion_detection等精简接口,并维护DETECTION_PASSIVE/DETECTION_ACTIVE检测状态。
这套设计把"换一颗芯片"的成本压缩为"新增一个 .c 文件 + 打开对应编译开关",上层代码完全不变。
Architecture
flowchart TD
subgraph sg_App["应用层 (App)"]
App["业务代码"]
API["gsensor_api.c<br/>sensor_init / sensor_data_get / sensor_motion_detection"]
end
subgraph sg_Mgr["管理层 (Manager)"]
Mgr["gSensor_manage.c<br/>gravity_sensor_init / get_gSensor_data"]
IIC["IIC 原语<br/>gravity_sensor_command / _gravity_sensor_get_ndata"]
Lock["spinlock_t sensor_iic 互斥锁"]
end
subgraph sg_Dev["设备层 (Device)"]
S1["msa310.c (MSA310)"]
S2["SC7A20_E.c / SC7A20_TR.c (SC7A20)"]
OM["mouse_sensor/OMSensor_manage.c<br/>+ hal3205.c (光学鼠标传感器)"]
end
subgraph sg_HW["硬件层"]
HW_IIC["IIC 总线 (IO_PORTA_08 SCL / IO_PORTA_07 SDA)"]
HW_INT["传感器 INT 中断 IO"]
end
App -->|"调用"| API
API -->|"G_SENSOR_INTERFACE 命令分发"| Mgr
Mgr --> IIC
Mgr --> Lock
IIC --> HW_IIC
S1 -->|"REGISTER_GRAVITY_SENSOR<br/>链接段 .gsensor_dev"| Mgr
S2 -->|"REGISTER_GRAVITY_SENSOR<br/>链接段 .gsensor_dev"| Mgr
OM -->|"REGISTER_OMSENSOR<br/>链接段 .omsensor_dev"| Mgr
S1 --> HW_INT
S2 --> HW_INT
OM --> HW_IIC
架构说明:
- 应用层只依赖
gsensor_api.h暴露的 5 个函数,不感知具体芯片;DETECTION_STATES(DETECTION_PASSIVE=0/DETECTION_ACTIVE)决定传感器是否处于主动上报运动中断的状态(见 gsensor_api.h)。 - 管理层是核心枢纽:持有全局句柄
G_SENSOR_INTERFACE *gSensor_hdl与运行时信息G_SENSOR_INFO __gSensor_info,所有 IIC 读写都先取sensor_iic自旋锁,保证与其它共享 IIC 的外设互斥(见 gSensor_manage.c)。 - 设备层通过编译开关裁剪:整个管理文件被
#if (TCFG_GSENSOR_ENABLE && (TCFG_SC7A20_EN || TCFG_SC7A20_E_EN || TCFG_MSA310_EN))包裹,未使能时整段代码不参与编译(见 gSensor_manage.c)。
G-Sensor 驱动框架详解
链接段注册机制(内核级插件式驱动)
G-Sensor 框架不维护任何驱动列表,而是利用链接脚本的段(section)实现"零注册代码"的插件机制:
#define REGISTER_GRAVITY_SENSOR(gSensor) \
static G_SENSOR_INTERFACE gSensor SEC_USED(.gsensor_dev)
#define list_for_each_gsensor(c) \
for (c=gsensor_dev_begin; c<gsensor_dev_end; c++)
设计意图:SEC_USED(.gsensor_dev) 把每个驱动实例放入链接器自定义段,链接器自动生成段边界符号 gsensor_dev_begin / gsensor_dev_end(在 gSensor_manage.h 中以 extern 声明)。list_for_each_gsensor(c) 则让管理层像遍历数组一样遍历所有已注册驱动。新增一颗传感器 = 新写一个 .c 文件 + 定义一个 G_SENSOR_INTERFACE 静态实例并用该宏注册,框架代码零改动。同样的模式被光学鼠标传感器复用(REGISTER_OMSENSOR 注册到 .omsensor_dev 段,见 OMSensor_manage.h)。
核心接口与数据结构
每个重力传感器驱动必须实现一个 G_SENSOR_INTERFACE:
typedef struct {
u8 logo[20]; // 驱动标识/名称
u8(*gravity_sensor_init)(void); // 芯片初始化
char (*gravity_sensor_check)(void); // 芯片存在性检查(读 ID)
int (*gravity_sensor_ctl)(u8 cmd, void *arg); // 命令分发入口
} G_SENSOR_INTERFACE;
gravity_sensor_ctl 是设备层唯一的总线入口,上层通过命令枚举驱动芯片行为:
enum {
GSENSOR_DISABLE = 0, // 关闭传感器(进入低功耗)
GSENSOR_RESET_INT, // 复位中断
GSENSOR_RESUME_INT, // 恢复中断使能
GSENSOR_INT_DET, // 中断检测(运动唤醒)
READ_GSENSOR_DATA, // 读取三轴原始数据
GET_ACCEL_DATA, // 获取加速度(含换算)
SEARCH_SENSOR, // 搜索/枚举传感器
};
设计意图:把"寄存器差异"封装进命令实现内部。例如 READ_GSENSOR_DATA 对 MSA310 与 SC7A20 的寄存器地址、数据格式完全不同,但管理层的 get_gSensor_data() 只需调用 gSensor_hdl->gravity_sensor_ctl(READ_GSENSOR_DATA, accel_data) 即可拿到统一的 axis_info_t 数组,实现芯片无关的数据消费。
配套数据结构:
axis_info_t { short x, y, z; }—— 单次采样三轴原始值(gSensor_manage.h#L29-L33);gsensor_platform_data { u8 iic; char gSensor_name[20]; u32 gSensor_int_io; }—— 平台配置:IIC 句柄、传感器名、中断 IO(gSensor_manage.h#L43-L47);G_SENSOR_INFO { iic_hdl, iic_delay, ctl_flag, init_flag, check_cnt, check_timer_hdl }—— 运行时状态,其中iic_delay是两字节数据之间的延时(不改变 IIC 时钟频率),check_cnt/check_timer_hdl用于定时轮询芯片存在性(gSensor_manage.h#L49-L56)。
IIC 读写原语(自旋锁保护的原子操作)
管理层提供两条对设备驱动透明的 IIC 原语。写寄存器:
u8 gravity_sensor_command(u8 w_chip_id, u8 register_address, u8 function_command)
{
spin_lock(&sensor_iic);
u8 ret = 1;
iic_start(gSensor_info->iic_hdl);
if (0 == iic_tx_byte(gSensor_info->iic_hdl, w_chip_id)) {
ret = 0;
log_info("\n gsen iic wr err 0");
goto __gcend;
}
delay(gSensor_info->iic_delay);
if (0 == iic_tx_byte(gSensor_info->iic_hdl, register_address)) {
ret = 0;
log_info("\n gsen iic wr err 1");
goto __gcend;
}
delay(gSensor_info->iic_delay);
if (0 == iic_tx_byte(gSensor_info->iic_hdl, function_command)) {
ret = 0;
log_info("\n gsen iic wr err 2\n");
goto __gcend;
}
__gcend:
iic_stop(gSensor_info->iic_hdl);
spin_unlock(&sensor_iic);
return ret;
}
写操作流程:iic_start → 发送器件写地址(w_chip_id)→ 发送寄存器地址 → 发送数据字节 → iic_stop。任一步 iic_tx_byte 返回 0 即判定失败并 goto 统一收尾(先 iic_stop 再释放锁,避免异常路径挂死总线),同时打印错误级日志 [GSENSOR_MAN] gsen iic wr err N。
读多字节(带 IIC 重复起始位,用于读取三轴连续寄存器):
u8 _gravity_sensor_get_ndata(u8 r_chip_id, u8 register_address, u8 *buf, u8 data_len)
{
spin_lock(&sensor_iic);
u8 read_len = 0;
iic_start(gSensor_info->iic_hdl);
if (0 == iic_tx_byte(gSensor_info->iic_hdl, r_chip_id - 1)) { ... }
delay(gSensor_info->iic_delay);
if (0 == iic_tx_byte(gSensor_info->iic_hdl, register_address)) { ... }
iic_start(gSensor_info->iic_hdl); // 重复起始
if (0 == iic_tx_byte(gSensor_info->iic_hdl, r_chip_id)) { ... }
delay(gSensor_info->iic_delay);
for (; data_len > 1; data_len--) {
*buf++ = iic_rx_byte(gSensor_info->iic_hdl, 1, NULL); // 前 N-1 字节带 ACK
read_len ++;
}
*buf = iic_rx_byte(gSensor_info->iic_hdl, 0, NULL); // 最后一字节 NACK
read_len ++;
__gdend:
iic_stop(gSensor_info->iic_hdl);
delay(gSensor_info->iic_delay);
spin_unlock(&sensor_iic);
return read_len;
}
设计意图:IIC 读需要"写寄存器地址 → 重复起始 → 读数据"三段式时序;读地址为 r_chip_id - 1(7 位地址左移后最低位为 0 表示写),读阶段地址为 r_chip_id(最低位为 1 表示读)。最后一个字节必须以 NACK(iic_rx_byte(..., 0, NULL))结束,否则从机不会释放总线。整个序列用 sensor_iic 自旋锁包裹,因为同一 IIC 总线可能被多个外设(如鼠标传感器)共享,非原子操作会导致总线时序错乱。
三轴数据导出
管理层把设备驱动的 axis_info_t 数组"扁平化"为上层易用的 short 缓冲:
int get_gSensor_data(short *buf)
{
axis_info_t accel_data[32];
int axis_info_len = gSensor_hdl->gravity_sensor_ctl(READ_GSENSOR_DATA, accel_data);
for (int i = 0; i < axis_info_len; i++) {
buf[i * 3] = accel_data[i].x;
buf[i * 3 + 1] = accel_data[i].y;
buf[i * 3 + 2] = accel_data[i].z;
}
return axis_info_len;
}
该函数返回采样点数,输出缓冲按 x,y,z 交错排列,一次最多导出 32 组采样(axis_info_t accel_data[32])。上层拿到的是统一格式,无论底层是 MSA310 还是 SC7A20。ACCEL_OF_GRAVITY(10) 与 ACCEL_DATA_GAIN(10) 两个宏(gSensor_manage.h#L13-L14)定义了 g 值与原始数据的换算基准,供 GET_ACCEL_DATA 命令做量纲换算。
初始化流程与传感器搜索
gravity_sensor_init(void *_data) 是管理层的初始化入口,_data 指向平台数据 struct gsensor_platform_data:
int gravity_sensor_init(void *_data)
{
if (sensor_iic_init_status == 0) {
spin_lock_init(&sensor_iic);
sensor_iic_init_status = 1;
}
gSensor_info->init_flag = 0;
int retval = 0;
platform_data = (const struct gsensor_platform_data *)_data;
gSensor_info->iic_hdl = platform_data->iic;
struct iic_master_config iic_config = { ... }; // 按平台数据配置 IIC 主机
...
}
初始化要点(结合源码可见部分与头文件声明):
- 自旋锁一次性初始化:
sensor_iic_init_status是模块级标志,保证sensor_iic锁只初始化一次(多实例/重复 init 安全); - 绑定平台数据:从
gsensor_platform_data取出iic句柄写入gSensor_info->iic_hdl,之后所有 IIC 原语都使用该句柄; - 设备搜索:初始化过程中通过
list_for_each_gsensor(c)遍历.gsensor_dev段,调用各驱动的gravity_sensor_check()探测芯片 ID,命中者被赋给全局句柄gSensor_hdl; - 失败重试:
G_SENSOR_INFO.check_cnt/check_timer_hdl表明框架支持定时器驱动的周期性存在性检查(对应命令SEARCH_SENSOR),芯片掉线后可自动重新搜索。
运动检测状态机
gsensor_api.h 定义了应用可见的检测状态:
typedef enum {
DETECTION_PASSIVE = 0, // 被动检测:不产生中断,上层按需读取
DETECTION_ACTIVE // 主动检测:芯片使能运动中断,唤醒后上报
} DETECTION_STATES;
static DETECTION_STATES detection_status = DETECTION_PASSIVE;
bool sensor_motion_detection(void) 切换/查询检测模式,配合命令枚举中的 GSENSOR_RESET_INT、GSENSOR_RESUME_INT、GSENSOR_INT_DET 使用。设计意图:在低功耗场景(如 FindMy 防丢器)中,默认 DETECTION_PASSIVE 让传感器处于低功耗待机,仅当需要监控移动时才切换为 DETECTION_ACTIVE,由芯片 INT 引脚唤醒 MCU,避免高频轮询消耗电流。
Core Flow
sequenceDiagram
participant App as 应用代码
participant API as gsensor_api.c
participant Mgr as gSensor_manage.c
participant Dev as 芯片驱动 (msa310/SC7A20)
participant IIC as IIC 总线 (iic_api)
participant HW as 传感器芯片
App->>API: sensor_init()
API->>Mgr: gravity_sensor_init(platform_data)
Mgr->>Mgr: 初始化 spinlock / iic_hdl
loop 遍历 .gsensor_dev 段
Mgr->>Dev: gravity_sensor_check()
Dev->>IIC: _gravity_sensor_get_ndata(读 ChipID 寄存器)
IIC->>HW: IIC 读序列 (start/addr/reg/restart/read)
HW-->>IIC: ChipID 数据
IIC-->>Dev: buf
Dev-->>Mgr: 命中则 gSensor_hdl = dev
end
Mgr-->>API: 初始化完成
API-->>App: 0
App->>API: sensor_data_get()
API->>Mgr: get_gSensor_data(buf)
Mgr->>Dev: gravity_sensor_ctl(READ_GSENSOR_DATA, accel_data)
Dev->>IIC: 读三轴连续寄存器 (spin_lock 保护)
IIC->>HW: 6+ 字节连续读
HW-->>IIC: x/y/z 原始数据
IIC-->>Dev: axis_info_t[]
Dev-->>Mgr: 采样点数
Mgr-->>API: 扁平化 short 缓冲
API-->>App: 三轴数据
App->>API: sensor_motion_detection()
API->>Mgr: gravity_sensor_ctl(GSENSOR_RESUME_INT/GSENSOR_INT_DET)
Mgr->>Dev: 配置 INT 寄存器
HW-->>Mgr: 运动触发 INT
Mgr-->>API: 检测结果 (DETECTION_ACTIVE)
流程要点: 初始化阶段的关键是"段遍历 + ChipID 探测"——框架不知道板子上焊了哪颗芯片,靠遍历注册表逐个读 ID 自动发现;数据读取阶段每笔 IIC 事务都被 sensor_iic 自旋锁原子化;运动检测阶段把中断控制交给芯片驱动,应用层只关心状态切换。
Usage Examples
应用层调用(G-Sensor)
int sensor_init();
int sensor_data_get();
void get_sensor_avg(axis_info_t *axis_avg);
int sensor_deinit();
bool sensor_motion_detection(void);
典型用法:开机调用 sensor_init();周期任务调用 sensor_data_get() 获取三轴原始数据,或用 get_sensor_avg(axis_avg) 获取多次采样平均(用于消抖/校准);低功耗场景先 sensor_motion_detection() 使能运动中断,收到唤醒事件后读取数据;关机时 sensor_deinit() 释放 IIC 与中断资源。
驱动注册示例(设备层)
#define REGISTER_GRAVITY_SENSOR(gSensor) \
static G_SENSOR_INTERFACE gSensor SEC_USED(.gsensor_dev)
#define GSENSOR_PLATFORM_DATA_BEGIN(data) \
static const struct gsensor_platform_data data = {
#define GSENSOR_PLATFORM_DATA_END() \
};
新增芯片驱动(如 msa310.c)时,定义 static G_SENSOR_INTERFACE 实例并用 REGISTER_GRAVITY_SENSOR 注册,实现 gravity_sensor_init(芯片寄存器配置)、gravity_sensor_check(读 ChipID)、gravity_sensor_ctl(命令 switch 分发,内部调用 gravity_sensor_command / _gravity_sensor_get_ndata 完成 IIC 访问)。
光学鼠标传感器(HID 报告格式)
// HID鼠标的报告格式
typedef struct {
uint8_t buttonMask; // 按钮状态,5位用于按钮,3位用于填充
uint8_t wheel; // 滚轮移动,8位
uint8_t xymovement[4]; // 数组来存放X和Y轴的移动量
} mouse_packet_data_t;
typedef struct {
uint8_t button_send_flag;
uint8_t wheel_send_flag;
uint8_t sensor_send_flag;
} mouse_send_flags_t;
鼠标传感器驱动把传感器位移数据打包为 HID 标准鼠标报告:buttonMask(5 位按钮 + 3 位填充)、wheel(滚轮增量)、xymovement[4](X/Y 位移,各 2 字节)。optical_mouse_read_sensor_handler_high() 负责把传感器数据填充进该结构,再交给上层 HID 上报;VECTOR_REVERS(vec) 宏用于处理传感器位移方向的取反。OMSENSOR_INTERFACE(OMSensor_manage.h#L29-L38)除运动读取外还暴露了 OMSensor_wakeup(唤醒传感器)、OMSensor_led_switch(LED 亮度控制)、OMSensor_set_cpi(灵敏度调节)等光学传感器特有能力,optical_mouse_sensor_timer_init/deinit 管理周期性采样定时器。
Configuration Options
| 配置项 | 类型 | 默认/取值 | 说明 |
|---|---|---|---|
TCFG_GSENSOR_ENABLE | 编译开关 | 0/1 | 总开关,关闭后整个 gSensor_manage.c 不参与编译(gSensor_manage.c#L19) |
TCFG_SC7A20_EN | 编译开关 | 0/1 | 使能 SC7A20 基础驱动(SC7A20_TR.c) |
TCFG_SC7A20_E_EN | 编译开关 | 0/1 | 使能 SC7A20 增强驱动(SC7A20_E.c) |
TCFG_MSA310_EN | 编译开关 | 0/1 | 使能 MSA310 驱动(msa310.c / msa310_function.c) |
GSENSOR_PRINTF_ENABLE | 编译开关 | 0/1 | 使能 [GSENSOR_MAN] 前缀的调试日志与 hexdump(gSensor_manage.c#L31-L38) |
IIC_SCL_IO | IO 宏 | IO_PORTA_08 | G-Sensor IIC 时钟引脚(gSensor_manage.h#L16) |
IIC_SDA_IO | IO 宏 | IO_PORTA_07 | G-Sensor IIC 数据引脚(gSensor_manage.h#L17) |
ACCEL_OF_GRAVITY | 常量 | 10 | 重力加速度换算基准(mg/LSB 相关),用于 GET_ACCEL_DATA |
ACCEL_DATA_GAIN | 常量 | 10 | 原始数据到加速度的增益系数 |
G_SENSOR_INFO.iic_delay | 运行时 | 10 | 两字节数据间的 IIC 延时(不影响时钟频率,只影响字节间隔),默认在 __gSensor_info 初始化中设置(gSensor_manage.c#L44) |
gsensor_platform_data.iic | 平台数据 | 由板级配置 | 指定 G-Sensor 挂载的 IIC 控制器句柄(gSensor_manage.h#L43-L47) |
平台数据通过 GSENSOR_PLATFORM_DATA_BEGIN/END 宏以静态常量方式定义;OMSENSOR_PLATFORM_DATA(OMSensor_manage.h#L22-L27)则声明了光学鼠标传感器的 sclk_io / data_io / int_io 引脚配置。
API Reference
G-Sensor 应用层(gsensor_api.h)
| 函数 | 参数 | 返回 | 说明 |
|---|---|---|---|
sensor_init() | 无 | int | 初始化 G-Sensor:初始化自旋锁、配置 IIC、搜索并绑定芯片驱动;0 表示成功 |
sensor_data_get() | 无 | int | 触发一次三轴数据读取并缓存,供 get_sensor_avg 使用 |
get_sensor_avg(axis_info_t *axis_avg) | axis_avg:三轴平均结果输出 | void | 对多次采样做平均,用于去抖/静态校准 |
sensor_deinit() | 无 | int | 反初始化:关闭传感器、释放 IIC/中断资源 |
sensor_motion_detection() | 无 | bool | 运动检测:切换/查询 DETECTION_PASSIVE / DETECTION_ACTIVE 状态 |
G-Sensor 管理层(gSensor_manage.h/c)
| 函数 | 参数 | 返回 | 说明 |
|---|---|---|---|
gravity_sensor_init(void *_data) | _data:gsensor_platform_data* | int | 绑定平台数据、配置 IIC 主机、执行传感器搜索 |
gsensor_disable() | 无 | int | 关闭 G-Sensor(对应 GSENSOR_DISABLE 命令) |
gravity_sensor_command(u8 w_chip_id, u8 reg, u8 cmd) | 写地址、寄存器地址、数据 | u8 | 带自旋锁的单字节 IIC 写;1 成功,0 失败 |
_gravity_sensor_get_ndata(u8 r_chip_id, u8 reg, u8 *buf, u8 len) | 读地址、寄存器、缓冲、长度 | u8 | 带自旋锁的 IIC 连续读(重复起始);返回实际读取字节数 |
get_gSensor_data(short *buf) | buf:输出缓冲 | int | 读取三轴数据,按 x/y/z 交错写入,返回采样点数 |
gsensor_io_ctl(u8 cmd, void *arg) | 命令、参数 | void | 透传调用 gSensor_hdl->gravity_sensor_ctl |
光学鼠标传感器(OMSensor_manage.h)
| 函数 | 说明 |
|---|---|
optical_mouse_sensor_init(OMSENSOR_PLATFORM_DATA *priv) | 初始化光学传感器(枚举 .omsensor_dev 段、绑定芯片驱动) |
optical_mouse_sensor_read_motion_handler(void *priv) | 定时器回调:读取位移增量 |
optical_mouse_read_sensor_handler_high(mouse_packet_data_t*, mouse_send_flags_t*) | 把传感器数据打包为 HID 鼠标报告 |
optical_mouse_sensor_set_cpi(u16) / clear_cpi(u16) | 设置/清除 CPI(灵敏度) |
optical_mouse_sensor_data_ready() | 查询数据就绪状态 |
optical_mouse_sensor_force_wakeup() | 强制唤醒传感器(退出休眠) |
optical_mouse_sensor_led_switch(u8) | 控制传感器 LED 开关 |
optical_mouse_sensor_timer_init/deinit() | 启动/停止采样定时器 |
get_optical_mouse_sensor_status() | 获取传感器在线状态 |
Failure Modes, Edge Cases & Concurrency
- IIC 写失败:
gravity_sensor_command中任一iic_tx_byte返回 0 即中断流程,打印gsen iic wr err N(N 表示失败阶段),并以goto统一执行iic_stop+spin_unlock。这种"先停总线再放锁"的顺序保证异常时不会把 IIC 总线锁死在中间状态。 - IIC 读失败:
_gravity_sensor_get_ndata在读地址/寄存器地址阶段失败时read_len = 0并跳转收尾,上层可通过返回值判断读操作是否完整。 - 并发访问:所有 IIC 事务都被模块级自旋锁
sensor_iic串行化;锁通过sensor_iic_init_status保证只初始化一次。若其它外设(如鼠标传感器)也使用同一 IIC 控制器,必须共用同一把锁,否则总线时序会被打断。 - 芯片不在线/掉线:
SEARCH_SENSOR命令与check_cnt/check_timer_hdl定时器支撑周期性探测;驱动未命中时gSensor_hdl为空,上层调用get_gSensor_data前应判空。 - 字节间时序:
iic_delay不改变 IIC 时钟频率,而是控制两字节数据间的延时——部分传感器(尤其较慢的 SC7A20 系)在连续读时要求字节间隔,过小会导致数据错位。出现"数据偶发跳变"时应优先排查该延时。 - 方向/符号边界:鼠标传感器位移可为负值,
VECTOR_REVERS(vec)(vec = ~vec; vec++,即取反加一)处理位移方向反转;xymovement[4]中 X/Y 各占 2 字节,需按有符号数解释,避免溢出截断。
Performance & Operational Notes
- 锁粒度:IIC 原语以"一次完整事务"为锁边界(start→读写→stop),而非逐字节加锁,兼顾了原子性与吞吐。
- 轮询与中断权衡:
DETECTION_PASSIVE模式下读取为显式轮询,适合低频采样;DETECTION_ACTIVE模式依赖芯片 INT 引脚唤醒,MCU 可深度睡眠,是低功耗产品(防丢器/手环)的首选路径。get_sensor_avg通过均值滤波降低噪声,但会引入采样延迟,需按应用场景调节采样次数。 - 调试开关:
GSENSOR_PRINTF_ENABLE使能后日志带[GSENSOR_MAN]前缀,且log_info_hexdump(put_buf)可直接打印 IIC 读到的原始寄存器内容,便于现场排查芯片 ID 与数据格式问题。 - 编译裁剪:管理文件整体受
TCFG_GSENSOR_ENABLE及芯片开关约束,未使能时不产生代码与数据,Flash/RAM 零开销;量产固件应关闭GSENSOR_PRINTF_ENABLE以减小镜像。
Extension Points
- 新增 G-Sensor 芯片:新建
.c文件实现G_SENSOR_INTERFACE(logo/gravity_sensor_init/gravity_sensor_check/gravity_sensor_ctl),用REGISTER_GRAVITY_SENSOR注册进.gsensor_dev段,打开对应TCFG_xxx_EN开关即可,框架与上层应用无需改动。 - 新增光学鼠标传感器:实现
OMSENSOR_INTERFACE(OMSensor_init/OMSensor_read_motion/OMSensor_data_ready/OMSensor_set_cpi等),用REGISTER_OMSENSOR注册进.omsensor_dev段;参考现有 HAL3205 驱动(apps/app/bsp/common/mouse_sensor/hal3205/hal3205.c)。 - 自定义平台数据:通过
GSENSOR_PLATFORM_DATA_BEGIN/END与OMSENSOR_PLATFORM_DATA在板级配置中声明 IIC 句柄、中断 IO 与传感器名称,驱动代码不感知具体管脚。 - 示例参考:FindMy 演示工程通过 UART 透传传感器数据的示例见
apps/demo/transfer/examples/findmy/ble_fmy_sensor_uart.c/h,可作为传感器数据对外上报(非 IIC 路径)的集成范本。
Related Links
- G-Sensor 应用层 API(gsensor_api.h)
- G-Sensor 管理层头文件(gSensor_manage.h)
- G-Sensor 管理层实现(gSensor_manage.c)
- 光学鼠标传感器管理(OMSensor_manage.h)
- HAL3205 光学传感器驱动
- FindMy 传感器 UART 透传示例
- 相关外设驱动页面(按键 / LED / IR)见各自目录:
apps/app/bsp/common/key、apps/app/bsp/common/led、apps/app/bsp/common/ir