HiSpark编程指南

- 调测资料
  - SDK Sample使用指南
    - 前言
    - 概述
    - Sample框架
    - 外设 Sample
    - SLE UART Sample
    - SLE UART DMA Sample
    - SLE Mouse Sample
    - SLE amic vdt with dongle
    - SLE microphone
    - SLE measure dis
    - BLE Lowpower Sample
    - BLE UART Sample
    - BLE Keyboard Sample
    - BLE SLE TAG Sample
    - BLE Mouse Sample
    - USB Keyboard Sample
    - USB Mouse Sample
    - USB AMIC VDT Sample
    - RCU
    - 多连接
    - air mouse
    - SLE MOUSE with Dongle
    - SLE OTA dongle
    - game mouse
    - BLE UUID
    - 缩略语
  - SDK二次开发网络安全注意事项
    - 前言
    - 产品安全解决方案
    - 结论
  - SDK开发环境搭建用户指南
    - 前言
    - 开发环境搭建
    - 编译SDK
    - 新建APP
  - 内存维测指导书
    - 前言
    - 简介
    - 内存维测功能
  - 射频AT测试指南
    - 前言
    - 射频测试AT指令
    - AT指令组包示例
    - 注意事项
  - 维测指导书
    - 前言
    - 概述
    - 日志说明
    - 日志分析
    - 问题定位
    - 典型案例
    - 常见问题FAQ
- 软件资料
  - AT命令使用指南
    - 前言
    - 指令说明
    - 通用AT指令
    - BT业务AT指令
    - 新增AT指令方法
  - DFU升级APK开发指南
    - 前言
    - 功能特性
    - 系统框架
    - 工程文件部署
    - 界面实现
    - 升级界面Fragment实现
    - 关于界面Fragment实现
    - 应用清单AndroidManifest
  - DFX软件开发指南
    - 前言
    - DIAG维测功能
    - 死机诊断
    - Dump解析
  - EFUSE使用指南
    - 前言
    - 概述
    - 使用软件接口读写EFUSE
    - 使用BurnTool烧写EFUSE
  - NFC TAG软件开发指导书
    - 前言
    - 概述
    - 开发指导
  - NV存储用户指南
    - 前言
    - NV简介
    - NV编译预置
    - NV API指南
    - 定制化NV结构使用介绍
    - 通过NV自定义传输数据最大长度
  - Release notes
    - 1.0.18版本描述文件
    - 1.0.17版本描述文件
    - 1.0.16版本描述文件
  - SDK开发指南
    - 前言
    - 概述
    - 系统接口
  - SDK接口变更说明书
    - 前言
    - BLE SDK接口变更说明
  - SP31 SLP使用指南
    - 开源版本
  - USB HID复合设备开发指南
    - 前言
    - 概述
    - 实现方式
  - 代码框架说明
    - 前言
    - SDK模块框架
    - SDK目录结构
    - SDK目录说明
  - 低功耗FAQ
    - 前言
    - 系统无法睡眠
    - IO无法唤醒系统
    - 唤醒后外设异常
    - 低功耗导致OTA升级异常
    - 功耗比预期高
    - 锁中断导致BT业务阻塞
    - 概率性狗超时
    - 供电模式&软件配置
    - 提前唤醒时间问题
  - 低功耗sample使用指南
    - 前言
    - 编译&测试
    - 系统状态管理
    - 用户指南
  - 低功耗开发指南
    - 前言
    - 低功耗概述
    - 低功耗方案
    - 开发指引
    - 功耗优化
    - 常见问题
  - 功率配置说明书
    - 前言
    - 射频功率配置说明
  - 升级方案使用指南
    - 前言
    - 概述
    - 升级包制作
    - 升级包传输
    - 升级包保存
    - 升级包本地升级
    - FAQ
  - 快速入门指南
    - 前言
    - 文档目录
    - 环境搭建
    - 基础功能开发
    - 调优
    - 特性开发
    - 测试报告
  - 星闪应用层SLE-Link协议
    - 前言
    - 概述
    - SLE Frame格式定义
    - 服务接口定义
    - 交互流程示例
  - 星闪软件开发指导书
    - 前言
    - 概述
    - Device Discovery接口
    - Connection Manager接口
    - SSAP Server接口
    - SSAP client接口
    - Low Latency接口
    - Transmission Manager接口
    - SLE Passkey接口
    - SLE Channel Map 接口
  - 红外学习使用指南
    - 前言
    - 红外学习方式
    - 通过比较器进行红外学习
    - 使用指南
  - 蓝牙软件开发指导书
    - 前言
    - 概述
    - GAP接口
    - GATT server接口
    - GATT client接口
  - 设备驱动开发指南
    - 前言
    - Pinctrl
    - GPIO
    - UART
    - SPI
    - I2C
    - ADC
    - DMA
    - PWM
    - WDT
    - Timer
    - Systick
    - TCXO
    - Flash
    - SFC
    - QDEC
    - KEYSCAN
    - PDM
    - USB_HID
    - USB_DFU
    - I2S
    - TRNG
    - 附：MIDDLEWARE
  - 音频编解码开发指导书
    - 前言
    - 概述
    - Audio Encoder
    - Audio Decoder
    - 调试与功能
- 工具资料
  - BurnTool工具使用指导书
    - 前言
    - BurnTool工具简介
    - 操作指南
    - FAQ
  - HiSpark Studio使用指南
    - 前言
    - 工具简介
    - 开发环境搭建
    - 工程管理
    - 工程配置
    - 编译运行
    - 软件包烧录
    - 栈分析和镜像分析
    - 工程调试
    - 串口控制台工具
    - Kconfig配置
    - GUI工程创建与使用
    - 高阶分析功能使用
    - 常见错误

### 该页内容

- 前言
- 红外学习方式
- 通过比较器进行红外学习
- 使用指南

# 前言

概述

BS2XV100通过API（Application Programming Interface）向开发者提供红外学习的相关接口，包括红外学习、发送以及恢复默认设置等相关接口。

读者对象

本文档主要适用于以下工程师：

- 技术支持工程师

- 软件开发工程师

- 软件测试工程师

符号约定

在本文中可能出现下列标志，它们所代表的含义如下。

符号 | 说明
--- | ---
| 表示如不避免则将会导致死亡或严重伤害的具有高等级风险的危害。
| 表示如不避免则可能导致死亡或严重伤害的具有中等级风险的危害。
| 表示如不避免则可能导致轻微或中度伤害的具有低等级风险的危害。
| 用于传递设备或环境安全警示信息。如不避免则可能会导致设备损坏、数据丢失、设备性能降低或其它不可预知的结果。 “须知”不涉及人身伤害。
| 对正文中重点信息的补充说明。 “说明”不是安全警示信息，不涉及人身、设备及环境伤害信息。

修改记录

文档版本 | 发布日期 | 修改说明
--- | --- | ---
03 | 2025-12-01 | 更新“通过比较器进行红外学习”章节内容。
02 | 2025-05-30 | 更新“红外学习方式”章节内容。 更新“通过比较器进行红外学习”章节内容。
01 | 2024-07-03 | 第一次正式版本发布。

# 红外学习方式

波形拷贝式学习方式

描述方法：载波频率+高低电平持续时间，也就是高低电平描述方式。

设计思想：把原遥控器所发出的信号进行完全拷贝，而不管遥控器是什么格式，目前将学习的波形数据通过SFC接口存储在memory_config_common.h中定义的IR Region地址中，在发送的时候读出数据进行发送。

优点：通用性非常好，可以描述一些协议，不用管逻辑值的定义。载波频率和高低电平也是非常容易获取的信息。

波形拷贝式学习方法解析

环境准备：红外接收器需要可以接受到红外载波并输入，载波周期和高低电平一起解析。

载波频率解析方法

计算两个下降沿时间差值。载波周期：Td1 – Td2 (us),载波频率：1/(Td1 – Td2) (us)。使用tcxo分别记录两次下降沿的时间，求差值，在学习结束之后将所有的差值进行平均值运算，计算出载波频率。

计算两个上升沿时间差值。载波周期：Tu1 – Tu2 (us),载波频率：1/(Td1 – Td2) (us)。使用tcxo分别记录两次下降沿的时间，求差值，在学习结束之后将所有的差值进行平均值运算，计算出载波频率。

图 1 红外载波计算

高低电平解析方法

1. 在Tu处记录当前时间，在计算完载波频率后，启动一个timer定时器，定时大约为两个载波周期的时间。

2. 在下一次gpio中断来的时候，清除并重启timer定时器。

3. 在进入timer定时器中断的时候就证明没有gpio中断了，红外波形进入低电平阶段了，这是计算下TH，并在第一次定时器中断的时候起一个超时定时器，设置时间为200ms，用作学习结束处理中断。

4. 在下一次进入gpio中断，计算下TL。然后重复步骤1~步骤3。

这样就计算出高低电平时间。

    static void ir_tickentry(void)
    {
        uint64_t current_time = uapi_tcxo_get_us();
        if (g_rx_count == 0) {
            g_carrier_timer = (g_carrier_timer / IR_TICK_COUNT) + IR_TIMER_DELAY;
        }
        g_rx_pattern[g_rx_count] = (int16_t)(current_time - IR_TIMER1_OUT_US + g_carrier_timer - g_rx_start);
        g_rx_count++;
        g_rx_start = current_time  - IR_TIMER1_OUT_US;
        g_timer_flag = true;
        if (g_rx_count == 1) {
            ir_clock_check_study_end(IR_TIMER0_OUT_US);
        }
    ir_port_tick_timer1_eoi_clr();
    osal_irq_clear(TIMER_1_IRQN);
    }

# 通过比较器进行红外学习

新增uapi_ir_study_by_cmp_start接口，需要硬件改板，暂时未在demo中使用，遥控器为了降低Bom成本的驱动下，尝试用芯片内比较器做红外RX，以减少板级有源管子的数量来降Bom成本。

红外Transceiver管R2经限流电阻R8接到红外控制IR_TX，在红外接收状态下IR_TX上拉高电平，D2在红外光照下会产生感光电压，该电压通过C22交流耦合至BS2X的比较器输入管脚，BS2X的芯片比较器需要直流偏置，通过20k/10k电阻产生1/3VDD的偏置，同时为了降低该电阻的静态漏电，可以通过GPIO来关断控制。

图 1 红外学习原理图

比较器学习方式

用比较器中断代替原有红外rx的gpio输入中断，解析方法与“红外学习方式”相同，其他操作不变，直接调用uapi_ir_study_by_cmp_start即可。

# 使用指南

1. 调用uapi_ir_study_start接口开始学习，参数为学习的按键键值。

       void uapi_ir_study_start(uint8_t key_value)
       {
           g_rx_key = key_value;
           g_rx_count = 0;
           g_rx_start = 0;
           g_carrier_count = 0;
           g_carrier_timer_start = 0;
           g_carrier_timer = 0;
           g_carrier_flag = false;
           g_timer_flag = false;
           (void)memset_s(g_rx_pattern, PATTERN_LEN * sizeof(int16_t), 0, PATTERN_LEN * sizeof(int16_t));
           ir_port_flash_reg_read(IR_FLASH_OFFSET, (uint8_t*)(g_study_tmp_buff[0]), 0xFA0);
           ir_port_flash_reg_erase(IR_FLASH_OFFSET, 0x1000);
           ir_flash_write(key_value, (uint32_t**)g_study_tmp_buff);
           ir_port_unregister_irq(TIMER_1_IRQN);
           ir_port_register_irq(TIMER_1_IRQN, (osal_irq_handler)ir_tickentry);
           ir_port_unregister_irq(TIMER_0_IRQN);
           ir_port_register_irq(TIMER_0_IRQN, (osal_irq_handler)ir_study_end_tickentry);
           ir_port_gpio_init();
           ir_port_unregister_irq(GPIO_0_IRQN);
           ir_port_register_irq(GPIO_0_IRQN, (osal_irq_handler)ir_gpio_irq_handler);
       }

2. timer1为载波超时中断，只有在大约两个载波周期没有来gpio中断后，才会进入timer1中断函数中，进入timer1中断函数时gpio电平为持续低电平，此时计算上一段高电平时间。

       static void ir_tickentry(void)
       {
           uint64_t current_time = uapi_tcxo_get_us();
           if (g_rx_count == 0) {
               g_carrier_timer = (g_carrier_timer / IR_TICK_COUNT) + IR_TIMER_DELAY;
           }
           g_rx_pattern[g_rx_count] = (int16_t)(current_time - IR_TIMER1_OUT_US + g_carrier_timer - g_rx_start);
           g_rx_count++;
           g_rx_start = current_time  - IR_TIMER1_OUT_US;
           g_timer_flag = true;
           if (g_rx_count == 1) {
               ir_clock_check_study_end(IR_TIMER0_OUT_US);
           }
           ir_port_tick_timer1_eoi_clr();
           osal_irq_clear(TIMER_1_IRQN);
       }

3. timer0为学习超时/结束中断，在第一次进入timer1中断函数是开始计时，时间为200ms，在该中断函数中将学习到的载波数据写入flash中。

       static void ir_study_end_tickentry(void)
       {
           ir_port_tick_timer1_disable();
           ir_port_tick_timer0_disable();
           ir_port_gpio_mask_interrupt();
           uint32_t addr = 0;
           rx_pattern_info_t rx_info;
           rx_info.length = (uint32_t)g_rx_count;
           g_rx_count = 0;
           rx_info.rx_buff = g_rx_pattern;
           uint32_t irq_sts = osal_irq_lock();
           for (uint8_t i = 0; i < IR_KEY_NUM; i++) {
               if (g_rx_key == g_rx_key_index[i]) {
                   addr = g_rx_addr_map[i];
                   break;
               }
       }

       ir_study_freq_set(&rx_info.freq);

       if (rx_info.freq == 0 && g_ir_cb_register_flag == true) {
           g_ir_callback(IR_STUDY_FAILURE);
       }

       if (addr < IR_FLASH_OFFSET || addr > IR_FLASH_OFFSET + IR_FLASH_LENGTH) {
           osal_irq_restore(irq_sts);
           ir_port_cmp_deinit();
           osal_irq_clear(TIMER_0_IRQN);
           osal_printk("ir study addr error:0x%x\n", addr);
           return;
       }
       ir_port_flash_reg_write(addr, (uint8_t *)(&rx_info),
                               sizeof(uint32_t) * IR_FLASH_REG_OFFSET);
       ir_port_flash_reg_write(addr + sizeof(uint32_t) * IR_FLASH_REG_OFFSET, (uint8_t *)
                              (rx_info.rx_buff), (rx_info.length) * sizeof(int16_t));
       if (g_ir_cb_register_flag == true) {
           g_ir_callback(IR_STUDY_SUCCESS);
       }
       osal_irq_restore(irq_sts);
       ir_port_tick_timer0_eoi_clr();
       osal_irq_clear(TIMER_0_IRQN);
       }

注意：当前留给红外学习的flash空间为4K，起始地址为IR_FLASH_OFFSET。