杰理 SDK 文档中心
首页
首页
  • 概述与快速入门

    • 芯片平台与 SDK 概述
    • 环境搭建与编译工具链
    • 快速开始:选型、编译与烧录
    • 烧录与量产工具
  • 构建系统与板级工程

    • 顶层 Makefile 与编译目标
    • 板级工程与配置
    • 后处理与配置工具
  • HID 人机交互应用

    • HID 应用架构总览
    • 键盘、翻页器与遥控应用
    • 鼠标应用:单模、双模与低延迟
    • 空闲应用与初始化流程
  • BLE 透传与数传应用

    • 透传应用总览
    • 多连接与无连接传输
    • AT 命令模组应用
    • Dongle 适配器应用
  • BSP 公共模块

    • 蓝牙公共处理
    • 按键、LED 与红外
    • 传感器与编码器
    • 存储、VM 与文件系统
    • 电源管理与低功耗
    • 消息调度与通信外设
  • 协议栈与预编译库

    • 蓝牙协议栈库
    • 设备驱动与文件系统库
    • 音频、升级与其他库
  • 开发资料与补丁发布

    • 文档资料中心
    • 版本补丁与兼容性修复

传感器与编码器

AW31N BLE SDK 中的外设输入/输出能力封装:光学鼠标传感器(OMSensor)管理层负责将光电位移传感器数据转换为 HID 鼠标报告,IR 编码器负责红外载波信号的调制与发送。本文档以源码为依据,说明两者的抽象模型、注册机制、数据流与控制流程。

Purpose and Scope

本页面覆盖 AW31N BLE SDK 中与"传感器与编码器"相关的两块子系统:

  1. 光学鼠标传感器管理(OMSensor_manage):位于 apps/app/bsp/common/mouse_sensor/,负责光电鼠标传感器驱动的注册、轮询、位移读取、滤波、HID 报告打包与 CPI 调节,是无线鼠标/带光标控制外设方案的核心。
  2. IR 编码器(ir_encoder):位于 apps/app/bsp/common/ir/ 与 apps/include_lib/common/ir_encoder.h,负责红外遥控载波信号的初始化、数据帧发送与重复码控制。

不在此页面范围内的相关主题:BLE HID 服务与上报链路的整体实现、GPIO/IOMUX 底层配置、系统定时器(sys_timer)机制,请参阅各自对应的目录页面。

Overview

在 AW31N 这样的单芯片 BLE 外设方案中,SDK 需要把"物理世界输入"(手指移动、滚轮滚动、按键按下)转化为"数字世界事件"(HID 报告、BLE 通知),并把数字指令重新转化为物理信号(红外载波)。

  • 传感器侧:光学鼠标传感器(如 PAW 系列)通过 SPI 类接口输出 X/Y 位移增量。SDK 用 OMSENSOR_INTERFACE 函数指针表抽象不同型号的差异,用 REGISTER_OMSENSOR 宏把驱动实例链接进 .omsensor_dev 段形成设备表,管理层通过 list_for_each_omsensor 遍历并选中可用设备。读取到的原始位移经过坐标交换、取反、均值滤波与饱和裁剪后,被打包进符合 HID 规范的 3 字节 xymovement 字段。
  • 编码器侧:IR 编码器把地址码/命令码按载波频率与占空比调制为红外脉冲序列,支持带重复码与不带重复码(HX 协议)两种发送模式,并提供停止重复码的接口。

两个子系统都遵循"配置开关 + 平台数据 + 接口抽象"的 SDK 通用模式:通过 TCFG_OMSENSOR_ENABLE 等宏裁剪代码,通过 *_PLATFORM_DATA 结构体描述硬件引脚,通过函数指针表解耦具体器件型号。

Architecture

flowchart TD
    subgraph sg_App["应用层 (App Task)"]
        AppTask["鼠标/HID 应用任务"]
    end

    subgraph sg_Manager["传感器管理层 (OMSensor_manage)"]
        Init["optical_mouse_sensor_init"]
        MotionHandler["optical_mouse_sensor_read_motion_handler"]
        HighHandler["optical_mouse_read_sensor_handler_high"]
        SetCpi["optical_mouse_sensor_set_cpi"]
        ForceWake["optical_mouse_sensor_force_wakeup"]
    end

    subgraph sg_DevTable["驱动设备表 (.omsensor_dev 段)"]
        DevTable["OMSensor_dev_begin ... OMSensor_dev_end"]
        DevA["OMSensor 驱动 A (OMSensor_init/read_motion...)"]
        DevB["OMSensor 驱动 B"]
    end

    subgraph sg_Platform["平台配置"]
        PlatData["OMSENSOR_PLATFORM_DATA<br/>(sclk/data/int IO + 型号 ID)"]
    end

    subgraph sg_IR["红外编码器 (ir_encoder)"]
        IRInit["ir_encoder_init(gpio, freq, duty)"]
        IRTx["ir_encoder_tx / ir_encoder_tx_hx"]
        IRStop["ir_encoder_repeat_stop"]
    end

    AppTask -->|"TCFG_OMSENSOR_ENABLE"| Init
    AppTask -->|"定时器回调"| MotionHandler
    AppTask -->|"HID 上报"| HighHandler
    AppTask --> SetCpi
    Init --> DevTable
    MotionHandler --> DevTable
    HighHandler --> DevTable
    Init --> PlatData
    DevTable --> DevA
    DevTable --> DevB
    AppTask -->|"红外遥控"| IRInit
    AppTask --> IRTx
    AppTask --> IRStop

架构要点:

  • 设备表(DevTable):REGISTER_OMSENSOR 宏通过 SEC_USED(.omsensor_dev) 把驱动实例放入专属链接段,OMSensor_dev_begin/OMSensor_dev_end 为该段的起止符号。这是一种零运行时注册开销的静态设备表模式——驱动"存在即注册",管理层用指针区间遍历即可发现所有驱动,无需维护运行时链表。
  • 接口抽象(OMSENSOR_INTERFACE):每个驱动只需实现 init / read_motion / data_ready / set_cpi / wakeup / led_switch / status_dump 等函数指针,管理层对所有型号一视同仁;具体型号差异(寄存器序列、SPI 时序)被完全封装在驱动内部。
  • 平台数据(PlatData):OMSENSOR_PLATFORM_DATA 描述传感器挂载的硬件资源(时钟脚、数据脚、中断脚、型号 ID),由板级配置通过 OMSENSOR_PLATFORM_DATA_BEGIN/END 宏声明,通过 optical_mouse_sensor_init 传入驱动。
  • IR 编码器:与应用层松耦合,应用只需调用 ir_encoder_init 配置发送脚与载波,再调用 ir_encoder_tx/ir_encoder_tx_hx 发送帧。

光学鼠标传感器管理(OMSensor_manage)深入分析

数据模型与类型定义

OMSensor_manage.h 定义了四个核心类型,它们共同构成传感器子系统的"契约":

// HID鼠标的报告格式
typedef struct {
    uint8_t buttonMask;        // 按钮状态,5位用于按钮,3位用于填充
    uint8_t wheel;             // 滚轮移动,8位
    uint8_t xymovement[3];         // 数组来存放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;

Source: OMSensor_manage.h

  • mouse_packet_data_t 是标准 HID 鼠标报告结构:buttonMask 承载 5 个按键位,wheel 为滚轮增量(8 位有符号),xymovement[3] 按 HID 规范的低字节/高半字节交错方式存放 12 位 X/Y 增量(见下文打包算法)。
  • mouse_send_flags_t 是应用侧与传感器层之间的"节流"握手标志:sensor_send_flag 置位表示上一帧尚未上报,新数据到达时先清零增量累加器,避免位移累积漂移。

平台数据与驱动接口是传感器层与具体器件解耦的关键:

typedef struct {
    u8 OMSensor_id[20];
    u8 OMSensor_sclk_io;
    u8 OMSensor_data_io;
    u8 OMSensor_int_io;
} OMSENSOR_PLATFORM_DATA;

typedef struct {
    u8 OMSensor_id[20];
    u8(*OMSensor_init)(OMSENSOR_PLATFORM_DATA *);
    u8(*OMSensor_read_motion)(s16 *, s16 *);
    u8(*OMSensor_data_ready)(void);
    u8(*OMSensor_status_dump)(void);
    void (*OMSensor_wakeup)(void);
    void (*OMSensor_led_switch)(u8);
    u16(*OMSensor_set_cpi)(u16 dst_cpi);
} OMSENSOR_INTERFACE;

Source: OMSensor_manage.h

设计意图: 把"驱动能力"抽象为纯函数指针集合,使管理层只依赖接口而不依赖型号。OMSensor_id 同时出现在平台数据与接口中,可用于运行时校验"这块板子的传感器型号"与"编译进固件的驱动"是否匹配;OMSensor_sclk_io/data_io/int_io 描述 SPI 时钟、数据与中断引脚,驱动据此完成 IO 初始化和中断使能。

设备注册机制(静态链接段设备表)

extern OMSENSOR_INTERFACE OMSensor_dev_begin[];
extern OMSENSOR_INTERFACE OMSensor_dev_end[];

#define REGISTER_OMSENSOR(OMSensor) \
	static OMSENSOR_INTERFACE OMSensor SEC_USED(.omsensor_dev)

#define list_for_each_omsensor(c) \
	for (c=OMSensor_dev_begin; c<OMSensor_dev_end; c++)

Source: OMSensor_manage.h

REGISTER_OMSENSOR 把驱动实例放入 .omsensor_dev 链接段:链接器将所有驱动实例连续排列,OMSensor_dev_begin 与 OMSensor_dev_end 自动界定区间。这样:

  • 零运行时开销:没有注册表初始化代码,设备发现即指针区间遍历;
  • 编译期裁剪:未编译进固件的驱动自然不会出现在区间内,SEC_USED 防止链接器把"看似无人引用"的驱动段丢弃;
  • 易扩展:新增传感器型号只需写一个 OMSENSOR_INTERFACE 实例并调用 REGISTER_OMSENSOR,管理层无需改动。

位移读取与坐标变换

传感器返回的原始位移在进入事件系统前会经过坐标变换,见 optical_mouse_sensor_read_motion_handler:

void optical_mouse_sensor_read_motion_handler(void *priv)
{
    s16 x = 0, y = 0;

    if (optical_mouse_sensor_data_ready()) {
        s16 temp = 0;
        if (OMSensor_hdl->OMSensor_read_motion) {
            OMSensor_hdl->OMSensor_read_motion(&x, &y);
        }

        temp = x;
        x = y;
        y = temp;

        VECTOR_REVERS(x);
        VECTOR_REVERS(y);
        mouse_optical_sensor_event(0, x, y);
    }
}

Source: OMSensor_manage.c

其中 VECTOR_REVERS(vec) 定义为 vec = ~vec; vec++,即对 16 位增量按位取反后加一——等价于取相反数(-vec)。传感器芯片安装方向不同会导致 X/Y 轴方向反转,这里统一在管理层做一次轴交换(temp = x; x = y; y = temp)与方向取反,把"硬件安装差异"收敛到一处,应用层无需感知。

optical_mouse_sensor_data_ready() 是内部辅助函数:若接口提供了 OMSensor_data_ready 则调用之,否则返回 0。这体现了函数指针接口的"可选能力"设计——不是所有传感器都有硬件就绪中断。

HID 报告打包算法(12 位增量编码)

optical_mouse_read_sensor_handler_high 是高频路径,负责把位移增量编码进 HID 报告:

void optical_mouse_read_sensor_handler_high(mouse_packet_data_t *mouse_packet, mouse_send_flags_t *mouse_flags)
{
    int16_t x = 0, y = 0;
    static int16_t delta_x = 0, delta_y = 0;

    if (optical_mouse_sensor_data_ready()) {
        int16_t temp = 0;
        if (OMSensor_hdl->OMSensor_read_motion) {
            OMSensor_hdl->OMSensor_read_motion(&x, &y);
        }

        temp = x;
        x = y;
        y = temp;

        VECTOR_REVERS(x);
        VECTOR_REVERS(y);

        if (mouse_flags->sensor_send_flag) {
            delta_x = 0;
            delta_y = 0;
        }

        if (((delta_x + x) >= -2047) && ((delta_x + x) <= 2047)) {
            // x值在有效范围内
        } else {
            x = 0;
        }

        if (((delta_y + y) >= -2047) && ((delta_y + y) <= 2047)) {
            // y值在有效范围内
        } else {
            y = 0;
        }

        // 坐标调整
        delta_x += (-y);
        delta_y += (x);

        mouse_packet->xymovement[0] = delta_x & 0xFF;
        mouse_packet->xymovement[1] = ((delta_y << 4) & 0xF0) | ((delta_x >> 8) & 0x0F);
        mouse_packet->xymovement[2] = (delta_y >> 4) & 0xFF;

        mouse_flags->sensor_send_flag = 0;
    }
}

Source: OMSensor_manage.c

关键算法解读:

  1. 增量累加(delta_x/delta_y):传感器两次读取间隔内可能移动多格,累加器把多次采样合并为一帧,保证不丢失位移;同时 static 变量跨调用保持状态。
  2. 饱和裁剪:HID 规范中 X/Y 增量为 12 位有符号数,范围 ±2047。若累加值超出范围,本帧丢弃该轴位移(置 0)而不是截断,避免产生方向错误的回跳。
  3. 轴旋转:delta_x += (-y); delta_y += (x) 等价于把原始位移旋转 90°(配合前面的轴交换与取反,完成传感器坐标系到屏幕坐标系的映射)。
  4. 12 位打包:xymovement[0] 存 X 低 8 位;xymovement[1] 高 4 位是 Y 低 4 位、低 4 位是 X 高 4 位;xymovement[2] 是 Y 高 8 位——正是 HID 鼠标报告对 12 位相对位移的标准字节序要求。
  5. 发送标志握手:打包完成后清 sensor_send_flag;应用侧若因 BLE 连接拥塞未能及时上报,会重新置位该标志,下一次采样时累加器清零,防止位移无限累积。

CPI 调节、唤醒与 LED 控制

u16 optical_mouse_sensor_set_cpi(u16 dst_cpi)
{
    u16 cpi = 0;
    log_info(">>>>>>>>>set cpi: %d\n", dst_cpi);
    if (OMSensor_hdl->OMSensor_set_cpi) {
        cpi = OMSensor_hdl->OMSensor_set_cpi(dst_cpi);
    }

    return cpi;
}

Source: OMSensor_manage.c

CPI(每英寸计数)决定鼠标灵敏度。管理层把目标值透传给驱动,驱动负责写入传感器寄存器并返回实际生效值——因为硬件可能只支持离散档位,返回值可能与目标值不同,应用层应使用返回值向用户反馈。optical_mouse_sensor_force_wakeup 与 optical_mouse_sensor_led_switch 同样通过 OMSensor_wakeup / OMSensor_led_switch 函数指针透传,分别用于低功耗模式下唤醒传感器和开关传感器内置 LED。

均值滤波

static s16 avg_filter(s16 *pdata, u8 num)
{
    u8 i = 0;
    s16 sum = 0;

    for (i = 0; i < num; i++) {
        sum += pdata[i];
    }

    return (sum / num);
}

Source: OMSensor_manage.c

avg_filter 对 num 个采样求算术平均,用于抑制传感器抖动噪声。它是 static 内部函数,说明滤波策略属于管理层私有实现——驱动只需要保证单次 read_motion 的原始准确性,平滑处理统一由管理层负责。

IR 编码器(ir_encoder)深入分析

IR 编码器把逻辑数据(地址码 + 命令码)调制为 38kHz 量级的红外载波脉冲序列,由 GPIO 驱动红外发射管。其公共接口定义在 apps/include_lib/common/ir_encoder.h,实现位于 apps/app/bsp/common/ir/ir_encoder.c,apps/app/bsp/cpu/periph_demo/ir_encoder_decoder.c 提供了发送/接收的演示用法。

公共 API

void ir_encoder_init(u32 gpio, u32 freq, u32 duty); //gpio:发送脚, freq:载波频率, duty:占空比,满量程10000
void ir_encoder_deinit();
u32 ir_encoder_tx(u8 ir_addr, u8 ir_cmd, u8 repeat_en); //addr:地址码, cmd:命令码, repeat_en:重复码发送使能
u32 ir_encoder_tx_hx(u8 ir_addr, u8 ir_cmd, u8 repeat_en);
void ir_encoder_repeat_stop();

Source: ir_encoder.h

参数语义:

  • ir_encoder_init(gpio, freq, duty):指定红外发射脚(GPIO)、载波频率(如 38000Hz)与占空比。duty 满量程为 10000,即 50% 占空比传 5000。占空比影响发射功率与功耗的权衡——占空比越高,发射距离越远但平均功耗越大。
  • ir_encoder_tx(addr, cmd, repeat_en):发送一帧 NEC 风格的红外遥控数据。repeat_en 为真时发送完命令帧后继续周期性发送重复码(用于按键长按场景,如音量持续调节),为假时仅发送单帧。
  • ir_encoder_tx_hx(addr, cmd, repeat_en):HX 协议变体,时序参数与 NEC 不同,用于兼容 HX 系列遥控接收头。
  • ir_encoder_repeat_stop():停止重复码发送,通常与按键释放事件绑定。
  • ir_encoder_deinit():释放 GPIO 与定时资源,进入低功耗前调用。

设计意图: 协议差异(NEC vs HX)通过两个独立入口 ir_encoder_tx / ir_encoder_tx_hx 暴露,而不是通过参数开关,因为二者的引导码与位时序完全不同;重复码的启停分离,使应用层可以把"长按持续发送"与"松开停止"表达为两个明确动作。

与传感器层的对比

IR 编码器是传感器系统的"镜像":传感器把物理位移→数字事件,编码器把数字命令→物理脉冲。两者都遵循 SDK 的 GPIO 平台配置风格,但编码器 API 更直接(无设备表抽象),因为红外协议栈相对固定,无需多器件适配。

核心控制流

传感器数据流:采样 → 坐标变换 → HID 打包 → 上报

sequenceDiagram
    participant T as 系统定时器 (sys_timer)
    participant M as OMSensor_manage 管理层
    participant D as 传感器驱动 (OMSENSOR_INTERFACE)
    participant A as HID 应用任务

    T->>M: 周期回调 optical_mouse_sensor_read_motion_handler
    M->>D: OMSensor_data_ready()
    D-->>M: 数据就绪标志
    M->>D: OMSensor_read_motion(&x, &y)
    D-->>M: 原始位移 x/y
    M->>M: 轴交换 + VECTOR_REVERS 取反
    M->>A: mouse_optical_sensor_event(0, x, y)
    A->>M: optical_mouse_read_sensor_handler_high
    M->>D: OMSensor_read_motion(&x, &y)
    M->>M: 增量累加 + ±2047 饱和裁剪 + 12位打包
    M-->>A: 填充 mouse_packet->xymovement[3]
    A->>A: 通过 BLE HID 上报

流程说明: 位移读取与 HID 打包是两个独立入口,可由不同上下文调用——read_motion_handler 由系统定时器驱动(低功耗友好,仅在需要时唤醒),handler_high 由应用上报任务驱动。两者共享 OMSensor_hdl 与静态累加器,因此调用频率必须匹配传感器数据率;若 sensor_send_flag 握手机制保证累加器不会因上报延迟而溢出。

红外发送流程:初始化 → 发帧 → 重复码 → 停止

flowchart TD
    Start([应用启动]) --> Init["ir_encoder_init(gpio, 38000, 5000)"]
    Init --> KeyDown{"按键事件?"}
    KeyDown -->|"单击"| Tx["ir_encoder_tx(addr, cmd, 0)<br/>单帧发送"]
    KeyDown -->|"长按"| TxHx["ir_encoder_tx_hx(addr, cmd, 1)<br/>帧 + 重复码"]
    Tx --> Done([发送完成])
    TxHx --> Repeat["ir_encoder_repeat_stop()<br/>按键释放时停止重复码"]
    Repeat --> Done
    Done --> Deinit["ir_encoder_deinit()<br/>进入低功耗前释放资源"]

流程说明: 单击场景关闭重复码,避免遥控器持续占用电波;长按场景开启重复码并持续到按键释放。deinit 放在低功耗流程中,释放 GPIO 与定时资源以降低休眠电流。

使用示例

注册一个光学鼠标传感器驱动

以下代码展示了如何基于 REGISTER_OMSENSOR 注册一个新传感器驱动(模式骨架,字段来自接口定义):

#include "OMSensor_manage.h"

static u8 my_sensor_init(OMSENSOR_PLATFORM_DATA *plat)
{
    /* 初始化 SPI 时钟脚/数据脚/中断脚,写入传感器寄存器序列 */
    return 1;
}

static u8 my_sensor_read_motion(s16 *x, s16 *y)
{
    /* 读取传感器位移寄存器,输出原始增量 */
    *x = read_register(REG_X);
    *y = read_register(REG_Y);
    return 1;
}

static u8 my_sensor_data_ready(void)
{
    /* 读取中断脚或状态寄存器判断数据是否就绪 */
    return gpio_read(OMSENSOR_INT_IO);
}

REGISTER_OMSENSOR(my_sensor_dev) = {
    .OMSensor_id        = "MY_SENSOR",
    .OMSensor_init      = my_sensor_init,
    .OMSensor_read_motion = my_sensor_read_motion,
    .OMSensor_data_ready  = my_sensor_data_ready,
    .OMSensor_set_cpi     = my_sensor_set_cpi,
};

Source: OMSensor_manage.h

REGISTER_OMSENSOR(my_sensor_dev) 会把该实例放入 .omsensor_dev 链接段,管理层通过 list_for_each_omsensor 自动发现它。示例中仅实现了必选回调,wakeup/led_switch/status_dump 等可选回调可省略(管理层调用前均判空)。

平台数据声明

板级配置使用宏声明传感器硬件资源:

OMSENSOR_PLATFORM_DATA_BEGIN(omsensor_platform_data)
    .OMSensor_id      = "MY_SENSOR",
    .OMSensor_sclk_io = IO_PORTA_00,
    .OMSensor_data_io = IO_PORTA_01,
    .OMSensor_int_io  = IO_PORTA_02,
OMSENSOR_PLATFORM_DATA_END()

Source: OMSensor_manage.h

OMSENSOR_PLATFORM_DATA_BEGIN/END 是标准的"初始化器括号"宏,保证所有板级配置书写格式一致;OMSensor_id 用于与驱动实例的型号 ID 匹配。

IR 编码器典型调用序列

#include "ir_encoder.h"

/* 系统初始化阶段:GPIO 发射脚、38kHz 载波、50% 占空比 */
ir_encoder_init(IO_PORTA_05, 38000, 5000);

/* 按键单击:发送地址 0x00、命令 0x45,不带重复码 */
ir_encoder_tx(0x00, 0x45, 0);

/* 按键长按:HX 协议,带重复码,松开时停止 */
ir_encoder_tx_hx(0x00, 0x45, 1);
...
ir_encoder_repeat_stop();

/* 进入低功耗前 */
ir_encoder_deinit();

Source: ir_encoder.h

配置选项

配置项类型默认值说明
TCFG_OMSENSOR_ENABLE宏开关未定义(按方案裁剪)编译开关:定义后编译 OMSensor_manage.c 全部逻辑,未定义时整个传感器管理层被排除在固件之外
OMSensor_sclk_iou8板级决定传感器 SPI 时钟引脚
OMSensor_data_iou8板级决定传感器 SPI 数据引脚
OMSensor_int_iou8板级决定传感器数据就绪中断引脚
OMSensor_idu8[20]板级决定传感器型号 ID 字符串,用于平台与驱动匹配
ir_encoder_init 的 frequ32调用方指定红外载波频率(Hz),典型值 38000
ir_encoder_init 的 dutyu32调用方指定载波占空比,满量程 10000,50% 即 5000
ir_encoder_tx 的 repeat_enu8调用方指定是否在命令帧后持续发送重复码

传感器管理层的裁剪机制值得注意:整个 .c 文件被 #ifdef TCFG_OMSENSOR_ENABLE 包裹(见 OMSensor_manage.c),未启用传感器功能的方案不会产生任何代码与链接段占用。

API 参考

传感器管理(OMSensor_manage)

函数签名说明
optical_mouse_sensor_initbool optical_mouse_sensor_init(OMSENSOR_PLATFORM_DATA *priv)遍历设备表选中匹配驱动并调用其 OMSensor_init;失败返回 false
optical_mouse_sensor_read_motion_handlervoid optical_mouse_sensor_read_motion_handler(void *priv)定时器回调:就绪时读取位移、坐标变换后发事件 mouse_optical_sensor_event
optical_mouse_read_sensor_handler_highvoid optical_mouse_read_sensor_handler_high(mouse_packet_data_t *mouse_packet, mouse_send_flags_t *mouse_flags)高频路径:累加增量、饱和裁剪并打包 HID 12 位位移字段
optical_mouse_sensor_set_cpiu16 optical_mouse_sensor_set_cpi(u16 dst_cpi)透传设置 CPI,返回实际生效值
get_optical_mouse_sensor_statusu8 get_optical_mouse_sensor_status(void)查询传感器状态(透传 OMSensor_status_dump)
optical_mouse_sensor_force_wakeupvoid optical_mouse_sensor_force_wakeup(void)唤醒休眠中的传感器(透传 OMSensor_wakeup)
optical_mouse_sensor_led_switchvoid optical_mouse_sensor_led_switch(u8 led_status)开关传感器内置 LED(透传 OMSensor_led_switch)

参数与返回约定:

  • optical_mouse_sensor_read_motion_handler(void *priv):priv 参数与 sys_timer 回调约定一致,实现中未使用,仅占位满足定时器回调签名。
  • optical_mouse_read_sensor_handler_high:mouse_packet 指向待填充的 HID 报告,mouse_flags 指向发送标志;函数内部会清零 sensor_send_flag,调用方无需额外处理。
  • optical_mouse_sensor_set_cpi:dst_cpi 为目标值(如 800/1600);返回值为驱动实际写入的 CPI,可能与目标不同(硬件档位限制),应用层应以返回值为准显示。

IR 编码器(ir_encoder)

函数签名说明
ir_encoder_initvoid ir_encoder_init(u32 gpio, u32 freq, u32 duty)配置发射脚、载波频率与占空比(满量程 10000)
ir_encoder_deinitvoid ir_encoder_deinit()释放红外发送资源
ir_encoder_txu32 ir_encoder_tx(u8 ir_addr, u8 ir_cmd, u8 repeat_en)发送 NEC 风格红外帧,repeat_en 控制重复码
ir_encoder_tx_hxu32 ir_encoder_tx_hx(u8 ir_addr, u8 ir_cmd, u8 repeat_en)发送 HX 协议红外帧
ir_encoder_repeat_stopvoid ir_encoder_repeat_stop()停止重复码发送

设计约束: ir_encoder_init 的 duty 参数满量程为 10000,因此 50% 占空比应传 5000,而非 50——这是 SDK 使用固定点表示的惯例,避免浮点运算。

故障模式、边界情况与并发性

传感器未就绪 / 驱动缺失

  • OMSensor_hdl 是 static 全局句柄,初始为 NULL。若设备表为空(未注册任何驱动)或 optical_mouse_sensor_init 未成功执行,optical_mouse_sensor_data_ready() 内部会因 OMSensor_hdl 为空而解引用崩溃——因此调用链要求 optical_mouse_sensor_init 必须先于任何读取回调执行,且返回 false 时应用不应启动读取任务。
  • 所有可选回调(wakeup、led_switch、status_dump、set_cpi)调用前都有判空保护(如 if (OMSensor_hdl->OMSensor_set_cpi)),缺省驱动不会导致崩溃,只是能力降级(例如无法调 CPI、无法唤醒)。

位移饱和与数据丢失

  • HID 12 位位移范围为 ±2047。当单帧内位移累加超出范围时,实现选择"整帧丢弃该轴"而非截断(OMSensor_manage.c)。原因:截断会产生方向错误的"回跳"光标,视觉上比丢帧更突兀;代价是高速甩动鼠标时部分位移被丢弃,属于有意的取舍。
  • sensor_send_flag 握手:若 HID 上报链路(BLE 连接)拥塞,应用置位该标志,下一次采样时 delta_x/delta_y 清零重来。这避免了累加器无限增长导致的溢出,但会丢失拥塞期间的位移。

定时器回调与上报回调的并发

read_motion_handler(定时器上下文)与 handler_high(上报上下文)共享 OMSensor_hdl 和静态累加器,但 SDK 中两者通常在同一任务上下文或由互斥的调度保证串行执行。若在 RTOS 多任务环境中同时调用,需要调用方自行保证互斥——源码层面没有加锁(这也是设计约束:单上下文、低延迟优先)。

IR 发送的时序敏感

红外协议是严格时序协议(位周期、引导码宽度、重复码间隔均以微秒计)。ir_encoder_tx/ir_encoder_tx_hx 发送期间应避免被高优先级任务抢占过长时间,否则接收端会解码失败。ir_encoder_repeat_stop 必须在按键释放事件中及时调用,否则重复码会持续占用发射资源并造成接收端持续响应(如音量一直增大)。

性能与运维注意事项

  • 读取频率与功耗:传感器管理层由系统定时器驱动,数据率可调。低功耗场景应降低轮询频率或依赖 OMSensor_int_io 中断(OMSensor_data_ready)唤醒;optical_mouse_sensor_force_wakeup 配合休眠策略可进一步降低平均电流。
  • CPU 占用:handler_high 是纯整数运算(移位、与、加法),无除法/浮点,可在高频路径上安全执行;avg_filter 含除法但仅用于低速滤波场景。
  • 调试手段:log_info(">>>>>>>>>set cpi: %d\n", dst_cpi)(OMSensor_manage.c)与 OMSensor_status_dump 提供了 CPI 写入与传感器状态的可观测性,量产调试时可通过串口日志确认驱动是否生效。
  • 裁剪收益:TCFG_OMSENSOR_ENABLE 编译开关保证未用传感器功能的固件零代码占用;IR 编码器同样仅在调用方编译 ir_encoder.c 时链接。

扩展点

  1. 新增传感器型号:实现 OMSENSOR_INTERFACE 函数指针表并 REGISTER_OMSENSOR,放置到 .omsensor_dev 段即完成接入;若板级使用新引脚组合,仅需修改 OMSENSOR_PLATFORM_DATA。
  2. 新增红外协议:参照 ir_encoder_tx / ir_encoder_tx_hx 的成对模式,在 ir_encoder.c 中增加协议入口,公共头文件同步声明;协议差异封装在实现内部,不影响调用方。
  3. 坐标映射定制:轴交换与 VECTOR_REVERS 集中在 read_motion_handler/handler_high 两处,若产品安装方向不同,只需调整这两处的变换顺序,无需改动驱动。
  4. 滤波策略替换:avg_filter 为 static 内部函数,可在不改接口的前提下替换为滑动平均/中值滤波等更强算法,作为管理层私有增强。

相关链接

  • OMSensor_manage.h(接口与宏定义)
  • OMSensor_manage.c(管理层实现)
  • ir_encoder.h(IR 编码器公共接口)
  • ir_encoder.c(IR 编码器实现)
  • ir_encoder_decoder.c(红外收发演示工程)

说明:IR 编码器实现细节(位时序、重复码间隔)位于 ir_encoder.c 与演示工程中,本文档依据公共头文件 ir_encoder.h 的 API 契约撰写;传感器驱动的具体寄存器操作属于各型号驱动私有实现,SDK 中以设备表形式链接。

Prev
按键、LED 与红外
Next
存储、VM 与文件系统