<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>中科蓝讯 on 栈外随记</title>
    <link>https://btio.cn/categories/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF/</link>
    <description>Recent content in 中科蓝讯 on 栈外随记</description>
    <image>
      <title>栈外随记</title>
      <url>https://btio.cn/images/logo-arong.svg</url>
      <link>https://btio.cn/images/logo-arong.svg</link>
    </image>
    <generator>Hugo</generator>
    <language>zh-CN</language><lastBuildDate>Mon, 17 Aug 2026 15:18:41 +0800</lastBuildDate>
    <atom:link href="https://btio.cn/categories/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>【中科蓝讯】耳机电量是怎样更新到手机的</title>
      <link>https://btio.cn/posts/2026/08/17/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF%E8%80%B3%E6%9C%BA%E7%94%B5%E9%87%8F%E6%98%AF%E6%80%8E%E6%A0%B7%E6%9B%B4%E6%96%B0%E5%88%B0%E6%89%8B%E6%9C%BA%E7%9A%84/</link>
      <pubDate>Mon, 17 Aug 2026 15:18:41 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/17/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF%E8%80%B3%E6%9C%BA%E7%94%B5%E9%87%8F%E6%98%AF%E6%80%8E%E6%A0%B7%E6%9B%B4%E6%96%B0%E5%88%B0%E6%89%8B%E6%9C%BA%E7%9A%84/</guid>
      <description>&lt;p&gt;从功能表面看，耳机电量更新并不复杂：ADC 采到电池电压，再查表换算成百分比，手机就能显示。&lt;/p&gt;
&lt;p&gt;但在实际项目中，电量更新并不是采样后直接查表：采样值要先经过滤波，查表得到的也只是参考电量，手机最终显示的还要由软件继续维护。先把实际电压、参考电量和显示电量这三个值分清楚，整条链路就容易理解了。&lt;/p&gt;
&lt;p&gt;先看完整链路：前 3 步把采样值处理成参考电量，后 3 步由软件维护显示值、完成双耳同步，最后上报手机。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;TWS 耳机电量更新六步流程&#34; loading=&#34;lazy&#34; src=&#34;https://btio.cn/images/csdn/163825929/%E7%94%B5%E9%87%8F%E6%9B%B4%E6%96%B0%E5%85%AD%E6%AD%A5%E6%B5%81%E7%A8%8B.png&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;电池电压是怎么采出来的&#34;&gt;电池电压是怎么采出来的？&lt;/h2&gt;
&lt;p&gt;顺着中科蓝讯 SDK 的调用关系看，系统会在 5ms 任务中检查 ADC 转换状态。转换完成后，读取电池通道和内部参考通道的 ADC 值。内部参考电压的已知值用于校准换算比例，再结合电池采样值和分压系数，得到实际电池电压。&lt;/p&gt;
&lt;h2 id=&#34;为什么采到电压后还不能直接使用&#34;&gt;为什么采到电压后还不能直接使用？&lt;/h2&gt;
&lt;p&gt;播放音乐或者蓝牙发射时，电池电压会短暂下降，负载变小后又会回升，所以一次采样不能直接拿来估算电量。&lt;/p&gt;
&lt;p&gt;结合耳机电压变化缓慢、但采样又容易受负载影响的特点，我更推荐用递推平均的方式更新电压：每来一个新采样值，只让平均电压向它靠近一小步。平均电压变化达到条件后，才更新稳定电压。下降条件更容易触发，上升条件更严格，可以减少电压回弹的影响。&lt;/p&gt;
&lt;h2 id=&#34;参考电量为什么不直接给手机&#34;&gt;参考电量为什么不直接给手机？&lt;/h2&gt;
&lt;p&gt;稳定电压通过“电压—电量对照表”换算成参考电量。它只用来判断当前显示偏高还是偏低，不是手机最终看到的电量。&lt;/p&gt;
&lt;p&gt;如果每次都用参考电量覆盖显示电量，负载变化仍然会造成百分比跳变。&lt;/p&gt;
&lt;h2 id=&#34;显示电量是怎么变化的&#34;&gt;显示电量是怎么变化的？&lt;/h2&gt;
&lt;p&gt;电量管理任务大约每 10 秒运行一次，并根据当前状态选择更新方式。&lt;/p&gt;
&lt;p&gt;放电时，参考电量低于显示电量，软件就让显示电量逐级下降，不会一次跳到参考值；电压短暂回升时，也不会把电量加回来。&lt;/p&gt;
&lt;p&gt;充电时，电压回升很快，不能直接用它增加百分比。软件根据充电电流和时间估算充入容量，再换算成显示电量；底层确认充满后，才显示 100%。&lt;/p&gt;
&lt;p&gt;温度决定当前能不能充电、使用多大的充电电流。低电提醒主要看显示电量，低压关机最终看实际电压。&lt;/p&gt;
&lt;h2 id=&#34;为什么内部电量和手机显示还可能不同&#34;&gt;为什么内部电量和手机显示还可能不同？&lt;/h2&gt;
&lt;p&gt;显示电量发生变化后会被保存，耳机下次开机可以从上次的结果继续计算。耳机内部按 1% 维护电量，当前 App 路径按 5% 一档显示。&lt;/p&gt;
&lt;p&gt;例如内部电量是 59%，手机仍然显示 60%；内部电量下降到 55% 后，手机才显示 55%。&lt;/p&gt;
&lt;h2 id=&#34;左右耳电量最后由谁上报&#34;&gt;左右耳电量最后由谁上报？&lt;/h2&gt;
&lt;p&gt;左右耳各自计算本耳电量，再通过 TWS 交换耳机和充电盒电量。主耳汇总左耳、右耳和充电盒数据后，统一上报给手机系统和配套 App。&lt;/p&gt;
&lt;p&gt;现在再看这条链路，重点就是分清三份数据：&lt;strong&gt;实际电压&lt;/strong&gt;反映电池状态，&lt;strong&gt;参考电量&lt;/strong&gt;判断变化方向，&lt;strong&gt;显示电量&lt;/strong&gt;负责最终呈现。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;原文：&lt;a href=&#34;https://blog.csdn.net/qq_56070244/article/details/163825929&#34;&gt;CSDN · 【中科蓝讯】耳机电量是怎样更新到手机的？&lt;/a&gt;
&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】耳机电量与充电问题复盘</title>
      <link>https://btio.cn/posts/2026/08/17/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF%E8%80%B3%E6%9C%BA%E7%94%B5%E9%87%8F%E4%B8%8E%E5%85%85%E7%94%B5%E9%97%AE%E9%A2%98%E5%A4%8D%E7%9B%98/</link>
      <pubDate>Mon, 17 Aug 2026 00:00:00 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/17/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF%E8%80%B3%E6%9C%BA%E7%94%B5%E9%87%8F%E4%B8%8E%E5%85%85%E7%94%B5%E9%97%AE%E9%A2%98%E5%A4%8D%E7%9B%98/</guid>
      <description>&lt;p&gt;本文先整理电压采样的方案设计思路，再复盘下面五类电量与充电问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#section-1&#34;&gt;电压稳定方案的设计思路&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-2&#34;&gt;电量曲线&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-3&#34;&gt;温度采样&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-4&#34;&gt;上报精度&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-5&#34;&gt;电量管控&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-6&#34;&gt;状态刷新&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、电压稳定方案的设计思路&lt;/h2&gt;
&lt;p&gt;电量计算不会直接使用一次 ADC 采样结果。播放音乐、蓝牙发射和负载切换都会造成短时波动，因此在进入电量估算前，需要先得到一份相对稳定的电压。&lt;/p&gt;
&lt;p&gt;这里先分清三个值：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;数据&lt;/th&gt;
					&lt;th&gt;含义&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;新采样电压&lt;/td&gt;
					&lt;td&gt;ADC 本次换算得到的电压，每次都可能变化&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;平均电压&lt;/td&gt;
					&lt;td&gt;经过滤波后缓慢上升或下降的中间值&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;稳定电压&lt;/td&gt;
					&lt;td&gt;达到更新条件后才改变，提供给后续电量算法使用&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;稳定电压不是手机显示的百分比，也不是永远固定不变。它只是在平均电压没有达到更新条件时保持原值。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方案一：去极值后取平均值&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先连续采集一组电压，去掉其中几个最大值和最小值，再对剩余数据求平均。偶发的异常高值和异常低值不会直接影响结果，适合 ADC 容易出现尖峰干扰的场景。&lt;/p&gt;
&lt;p&gt;这种方案的优点是抗单点异常能力强，思路也比较直观。缺点是需要保存一组采样数据，还要完成极值查找或排序，会占用更多内存和计算资源。采样窗口越长，结果越稳定，但输出延迟也越大，真实电压发生变化时不容易及时跟上。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方案二：递推平均加更新阈值&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;递推平均不需要保存整组数据。每得到一个有效采样值，就让原来的平均电压向新采样值移动一小步。新采样值低于旧平均值时向下移动，高于旧平均值时向上移动，每次移动的大小由两者差值和递推系数决定。&lt;/p&gt;
&lt;p&gt;这种方案的优点是内存和计算开销小，适合持续采样，也可以通过调整递推系数改变响应速度。缺点是它不会自动剔除异常值，一个突发尖峰会影响后续一段时间；系数过小，电压变化跟得慢，系数过大，又容易把短时波动带进结果。&lt;/p&gt;
&lt;p&gt;以递推系数 &lt;code&gt;1/32&lt;/code&gt; 为例：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当前平均电压和稳定电压都是 &lt;code&gt;4000mV&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;本次新采样电压为 &lt;code&gt;3800mV&lt;/code&gt;，与平均电压相差 &lt;code&gt;200mV&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;本次只向下移动差值的 &lt;code&gt;1/32&lt;/code&gt;，也就是 &lt;code&gt;6.25mV&lt;/code&gt;，新的平均电压约为 &lt;code&gt;3993.75mV&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;新的平均电压再与稳定电压比较。两者向下相差约 &lt;code&gt;6.25mV&lt;/code&gt;，达到当前项目的下降更新条件，稳定电压才更新。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果旧平均电压低于新采样电压，处理方向相反：旧平均电压加上两者差值的 &lt;code&gt;1/32&lt;/code&gt;，得到新的平均电压。无论差值大小，每次采样都会参与递推；更新阈值只负责决定稳定电压是否改变。&lt;/p&gt;
&lt;p&gt;当前项目在平均电压下降约 &lt;code&gt;5mV&lt;/code&gt; 时允许更新稳定电压，向上则需要变化约 &lt;code&gt;10mV&lt;/code&gt;。下降阈值更小，可以及时反映放电趋势；上升阈值更大，可以减少负载解除后电压回弹带来的影响。&lt;/p&gt;
&lt;p&gt;对于 TWS 耳机这种电压变化缓慢、需要持续采样的设备，递推平均占用资源少，也方便调整响应速度，当前 SDK 采用的就是第二种方案。如果还会出现明显的异常采样，可以先剔除异常值，再进入递推平均。&lt;/p&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、电量曲线&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;耳机从满电持续播放音乐时，有些 &lt;code&gt;10%&lt;/code&gt; 很快掉完，有些区间长时间不变化；左右耳在同一电量区间的续航也可能相差较大。总续航接近要求，但百分比曲线不均匀。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题原因&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这次问题的直接原因是软件仍使用旧电池的电压—电量表，与当前电芯的充放电曲线不匹配，导致各百分比区间对应的实际续航不均匀。左右耳的主从角色还会带来功耗差异，因此双耳验证时也要把主从切换考虑进去。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;处理结果&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;根据当前电芯的实测充放电数据更新电量表，同时调整电量相关的主从切换逻辑。修改后分别在 ANC 开启和关闭场景下完成整轮放电验证，各 &lt;code&gt;10%&lt;/code&gt; 区间的续航恢复到要求范围内。&lt;/p&gt;
&lt;h2 id=&#34;section-3&#34;&gt;三、温度采样&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】机盒通信问题单复盘</title>
      <link>https://btio.cn/posts/2026/08/16/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF%E6%9C%BA%E7%9B%92%E9%80%9A%E4%BF%A1%E9%97%AE%E9%A2%98%E5%8D%95%E5%A4%8D%E7%9B%98/</link>
      <pubDate>Sun, 16 Aug 2026 00:00:00 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/16/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF%E6%9C%BA%E7%9B%92%E9%80%9A%E4%BF%A1%E9%97%AE%E9%A2%98%E5%8D%95%E5%A4%8D%E7%9B%98/</guid>
      <description>&lt;p&gt;本文围绕下面四类问题，整理对应的现象、问题原因和处理结果：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#section-1&#34;&gt;快速开关盖后异常断连&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-2&#34;&gt;左右耳回复不一致，盒子灯效频闪&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-3&#34;&gt;耳机已经进入配对，盒子却不显示配对灯效&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-4&#34;&gt;配对超时后，盒子显示了错误灯效&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、快速开关盖后异常断连&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;快速开关盖时，耳机低概率断开。日志中没有完整的关盖命令，耳机却进入了充电状态。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题原因&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;快速开关盖时，充电盒重新拉起供电。电平持续时间超过原来的防抖阈值，耳机提前判断为稳定入盒。状态切换由充电检测触发，不是串口丢包。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;处理结果&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;将充电电平持续检测时间从约 &lt;code&gt;600 ms&lt;/code&gt; 延长到 &lt;code&gt;1 s&lt;/code&gt;，避免短暂电平触发充电模式。连续快速开关盖验证后，未再出现异常断连。&lt;/p&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、左右耳回复不一致，盒子灯效频闪&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;耳机进入配对状态并放入盒内后，充电盒白灯闪烁不均匀，部分场景出现频闪或亮度异常。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题原因&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;左右耳返回给充电盒的配对状态不一致。盒子先收到一只耳机的配对状态，又收到另一只耳机的非配对状态，灯效状态被反复切换。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;左耳回复：配对中
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;右耳回复：非配对
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;盒子灯效在两个状态之间反复切换
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;处理结果&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;由从耳同步主耳的配对状态，确保左右耳向充电盒返回一致的数据。双耳状态一致后，配对灯效恢复正常。&lt;/p&gt;
&lt;h2 id=&#34;section-3&#34;&gt;三、耳机进入配对，盒子却不显示配对灯效&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;耳机已经连接一台手机，再进入配对模式并放入盒内。充电盒只显示耳机电量，没有显示配对灯效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题原因&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;耳机同时存在“已连接”和“可发现可连接”两个状态。原来的回复逻辑优先返回已连接状态，覆盖了正在配对的状态，盒子因此没有进入配对灯效。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* 修改前 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;phone_connected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;report_connected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;pairing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;report_pairing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cm&#34;&gt;/* 修改后 */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;pairing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;report_pairing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;phone_connected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;report_connected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;处理结果&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;回复状态时优先判断配对状态，再判断手机连接状态。修改后，耳机在已连接状态下重新进入配对，盒子能够正常显示配对灯效。&lt;/p&gt;
&lt;h2 id=&#34;section-4&#34;&gt;四、配对超时后，盒子显示了错误灯效&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;耳机在配对状态下入盒，配对超时后，充电盒显示了配对成功灯效，而不是结束灯效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题原因&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;耳机在配对超时后返回无效状态，充电盒却把这个状态按配对成功处理。数据传输正常，但两端对同一状态的含义没有统一。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;处理结果&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;统一耳机与充电盒对回复状态的定义。收到无效状态时直接结束灯效，不再进入配对成功灯效。&lt;/p&gt;
&lt;p&gt;这四类问题对应的检查入口是：&lt;strong&gt;充电电平、左右耳回复是否一致、状态判断优先级、两端状态定义。&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】TWS 耳机机盒通信链路</title>
      <link>https://btio.cn/posts/2026/08/15/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA%E6%9C%BA%E7%9B%92%E9%80%9A%E4%BF%A1%E9%93%BE%E8%B7%AF/</link>
      <pubDate>Sat, 15 Aug 2026 00:00:00 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/15/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA%E6%9C%BA%E7%9B%92%E9%80%9A%E4%BF%A1%E9%93%BE%E8%B7%AF/</guid>
      <description>&lt;p&gt;机盒通信的主链路是：UART 收包、协议解析、校验与寻址、命令分发、业务执行、应答返回。这篇记录我负责该模块时使用的应用层调用链。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、UART 收包与协议解析&lt;/h2&gt;
&lt;div class=&#34;mermaid-diagram&#34; role=&#34;group&#34; aria-label=&#34;流程图&#34;&gt;
  &lt;pre class=&#34;mermaid&#34;&gt;%%{init: {&amp;#34;flowchart&amp;#34;: {&amp;#34;htmlLabels&amp;#34;: true, &amp;#34;wrappingWidth&amp;#34;: 1000, &amp;#34;rankSpacing&amp;#34;: 30}, &amp;#34;themeVariables&amp;#34;: {&amp;#34;lineColor&amp;#34;: &amp;#34;#71829a&amp;#34;}}}%%
flowchart LR
    N0[&amp;#34;充电盒发送数据&amp;#34;]
    N1[&amp;#34;UART 中断接收字节&amp;lt;br/&amp;gt;并写入环形缓冲区&amp;#34;]
    N2[&amp;#34;协议状态机&amp;lt;br/&amp;gt;拼接完整数据包&amp;#34;]
    N3[&amp;#34;应用任务&amp;lt;br/&amp;gt;从缓冲区取包并校验&amp;#34;]
    N0 --&amp;gt; N1
    N1 --&amp;gt; N2
    N2 --&amp;gt; N3
    class N0,N1,N2,N3 blue;
    classDef blue fill:#e8f2ff,stroke:#7ba4d8,color:#23456b,stroke-width:1.5px;
    classDef green fill:#e7f4ec,stroke:#78aa8b,color:#24563a,stroke-width:1.5px;
    classDef amber fill:#fff3df,stroke:#d5a25b,color:#72501e,stroke-width:1.5px;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;UART 中断侧负责收字节和快速识别数据包：&lt;/p&gt;
&lt;div class=&#34;mermaid-diagram&#34; role=&#34;group&#34; aria-label=&#34;流程图&#34;&gt;
  &lt;pre class=&#34;mermaid&#34;&gt;%%{init: {&amp;#34;flowchart&amp;#34;: {&amp;#34;htmlLabels&amp;#34;: true, &amp;#34;wrappingWidth&amp;#34;: 1000, &amp;#34;rankSpacing&amp;#34;: 30}, &amp;#34;themeVariables&amp;#34;: {&amp;#34;lineColor&amp;#34;: &amp;#34;#71829a&amp;#34;}}}%%
flowchart TB
    subgraph R0[&amp;#34;① 中断与接收回调&amp;#34;]
        direction LR
        R0N0[&amp;#34;uart1_isr()&amp;#34;]
        R0N1[&amp;#34;uart1_rx_done_cb()&amp;#34;]
        R0N0 --&amp;gt; R0N1
    end
    class R0N0,R0N1 blue;
    style R0 fill:#f1f6fd,stroke:#b8cfe8,color:#23456b
    subgraph R1[&amp;#34;② 解析与接收处理&amp;#34;]
        direction LR
        R1N0[&amp;#34;bsp_uart_packet_parse()&amp;#34;]
        R1N1[&amp;#34;charge_box_packet_recv()&amp;#34;]
        R1N0 --&amp;gt; R1N1
    end
    class R1N0,R1N1 green;
    style R1 fill:#f0f8f3,stroke:#b9d7c4,color:#24563a
    R0 --&amp;gt; R1
    classDef blue fill:#e8f2ff,stroke:#7ba4d8,color:#23456b,stroke-width:1.5px;
    classDef green fill:#e7f4ec,stroke:#78aa8b,color:#24563a,stroke-width:1.5px;
    classDef amber fill:#fff3df,stroke:#d5a25b,color:#72501e,stroke-width:1.5px;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;中断将字节写入环形缓冲区，快速解析一次以记录收包时间及开盖、心跳等标志。&lt;code&gt;charge_box_ssw_process(charge_sta)&lt;/code&gt; 再从缓冲区取出字节，重新组包、校验并处理命令。&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】TWS音量是怎样同步的</title>
      <link>https://btio.cn/posts/2026/08/14/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws%E9%9F%B3%E9%87%8F%E6%98%AF%E6%80%8E%E6%A0%B7%E5%90%8C%E6%AD%A5%E7%9A%84/</link>
      <pubDate>Fri, 14 Aug 2026 10:38:44 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/14/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws%E9%9F%B3%E9%87%8F%E6%98%AF%E6%80%8E%E6%A0%B7%E5%90%8C%E6%AD%A5%E7%9A%84/</guid>
      <description>&lt;p&gt;这篇梳理音乐音量在应用层的处理：手机下发的音量怎样换算为本地等级，左右耳怎样同时生效，以及耳机按键调节后怎样同步手机音量条。通话音量只作区分，不展开 HFP 细节。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、手机调节音量后的主流程&lt;/h2&gt;
&lt;p&gt;一次音量调节要对应下面四处状态：&lt;/p&gt;
&lt;div class=&#34;mermaid-diagram&#34; role=&#34;group&#34; aria-label=&#34;流程图&#34;&gt;
  &lt;pre class=&#34;mermaid&#34;&gt;%%{init: {&amp;#34;flowchart&amp;#34;: {&amp;#34;htmlLabels&amp;#34;: true, &amp;#34;wrappingWidth&amp;#34;: 1000, &amp;#34;rankSpacing&amp;#34;: 30}, &amp;#34;themeVariables&amp;#34;: {&amp;#34;lineColor&amp;#34;: &amp;#34;#71829a&amp;#34;}}}%%
flowchart LR
    N0[&amp;#34;手机音量条&amp;#34;]
    N1[&amp;#34;耳机内部音量级&amp;#34;]
    N2[&amp;#34;左右耳同步状态&amp;#34;]
    N3[&amp;#34;DAC 实际输出音量&amp;#34;]
    N0 --&amp;gt; N1
    N1 --&amp;gt; N2
    N2 --&amp;gt; N3
    class N0,N1,N2,N3 blue;
    classDef blue fill:#e8f2ff,stroke:#7ba4d8,color:#23456b,stroke-width:1.5px;
    classDef green fill:#e7f4ec,stroke:#78aa8b,color:#24563a,stroke-width:1.5px;
    classDef amber fill:#fff3df,stroke:#d5a25b,color:#72501e,stroke-width:1.5px;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;手机和耳机的音量范围并不相同。AVRCP 绝对音量是 0～127，SDK 内部可配置为 16 级或 32 级，最后还要通过音量表转换为 DAC 使用的数字音量。&lt;/p&gt;
&lt;p&gt;因此，手机显示 50% 不等于把数值 50 直接写入 DAC，而是先换算、再查表。&lt;/p&gt;
&lt;p&gt;这里的“绝对音量”指手机音量条和耳机媒体音量相互对应：任一侧调节，另一侧都要更新。&lt;/p&gt;
&lt;p&gt;手机调节音乐音量后，协议栈将新的媒体音量交给应用层，主线如下：&lt;/p&gt;
&lt;div class=&#34;mermaid-diagram&#34; role=&#34;group&#34; aria-label=&#34;流程图&#34;&gt;
  &lt;pre class=&#34;mermaid&#34;&gt;%%{init: {&amp;#34;flowchart&amp;#34;: {&amp;#34;htmlLabels&amp;#34;: true, &amp;#34;wrappingWidth&amp;#34;: 1000, &amp;#34;rankSpacing&amp;#34;: 30}, &amp;#34;themeVariables&amp;#34;: {&amp;#34;lineColor&amp;#34;: &amp;#34;#71829a&amp;#34;}}}%%
flowchart TB
    subgraph R0[&amp;#34;① 接收与换算&amp;#34;]
        direction LR
        R0N0[&amp;#34;收到手机设置的媒体音量&amp;#34;]
        R0N1[&amp;#34;把 0～127&amp;lt;br/&amp;gt;换算为本地音量级&amp;#34;]
        R0N2[&amp;#34;发起 TWS 定时音量请求&amp;#34;]
        R0N0 --&amp;gt; R0N1 --&amp;gt; R0N2
    end
    class R0N0,R0N1,R0N2 blue;
    style R0 fill:#f1f6fd,stroke:#b8cfe8,color:#23456b
    subgraph R1[&amp;#34;② 定时执行与保存&amp;#34;]
        direction LR
        R1N0[&amp;#34;左右耳在约定时间设置 DAC&amp;#34;]
        R1N1[&amp;#34;更新当前音量&amp;lt;br/&amp;gt;后续再延时保存&amp;#34;]
        R1N0 --&amp;gt; R1N1
    end
    class R1N0,R1N1 green;
    style R1 fill:#f0f8f3,stroke:#b9d7c4,color:#24563a
    R0 --&amp;gt; R1
    classDef blue fill:#e8f2ff,stroke:#7ba4d8,color:#23456b,stroke-width:1.5px;
    classDef green fill:#e7f4ec,stroke:#78aa8b,color:#24563a,stroke-width:1.5px;
    classDef amber fill:#fff3df,stroke:#d5a25b,color:#72501e,stroke-width:1.5px;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;TWS 路径不会在当前回调里直接设置 DAC，而是先发起定时请求；到达定时点后，左右耳再一起设置实际音量。&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】TWS 耳机音量问题单复盘</title>
      <link>https://btio.cn/posts/2026/08/14/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA%E9%9F%B3%E9%87%8F%E9%97%AE%E9%A2%98%E5%8D%95%E5%A4%8D%E7%9B%98/</link>
      <pubDate>Fri, 14 Aug 2026 00:00:00 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/14/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA%E9%9F%B3%E9%87%8F%E9%97%AE%E9%A2%98%E5%8D%95%E5%A4%8D%E7%9B%98/</guid>
      <description>&lt;h2 id=&#34;preface&#34;&gt;写在开始&lt;/h2&gt;
&lt;p&gt;这篇文章整理项目中比较有代表性的音量问题单。只保留已经写明原因和处理方向的内容，并按音量控制链路归为三类。&lt;/p&gt;
&lt;p&gt;排查音量问题时，可以先确认四个结果：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1. 收到的目标音量是否正确
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2. 左右耳保存的音量是否一致
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3. 两耳应用音量的时间是否一致
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4. 实际写入硬件的音量是否正确
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本文围绕下面三类问题，整理对应的现象和排查思路：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;#section-1&#34;&gt;回连后的初始音量不对&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-2&#34;&gt;快速调节时双耳音量不同步&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-3&#34;&gt;出盒恢复时单耳突然变大&lt;/a&gt;
。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、回连后的初始音量不对&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;耳机重新组队并连接手机后，预期使用耳机的默认音量，实际却变成手机之前保存的最大音量。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查思路 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;按时间确认三个值：重新组队后恢复的默认音量、手机首次下发的历史音量，以及最终实际生效的音量。若默认值恢复正确，但最终值变成手机历史值，说明问题出在手机下发后的覆盖。&lt;/p&gt;
&lt;p&gt;耳机重新组队后已恢复到默认音量，但手机连接时下发的历史音量又覆盖了本地值。如果要求重新组队后采用耳机默认音量，可以在首次连接的短暂保护窗口内忽略手机下发的音量，使用耳机的默认音量，同时将默认音量写回给 App。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题现象 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一拖二场景下，设备 A 已连接耳机且音量调到最大；设备 B 的音量较小，在播放音乐时抢连耳机。抢连成功后，音乐开始时仍按设备 A 的最大音量播放，随后才突然降到设备 B 的低音量。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查思路 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;检查抢连后耳机采用的是哪台设备的音量，以及 A2DP 开始播放前硬件音量是否已经更新。&lt;/p&gt;
&lt;p&gt;已有问题中，设备 B 抢连成功后，硬件音量设置晚于音乐开始，导致开始播放时仍保留设备 A 的最大音量。处理时要在 A2DP 出声前，按设备 B 的音量设置好硬件音量。&lt;/p&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、快速调节时双耳音量不同步&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;长按手机音量键或快速滑动音量条时，声音会短暂偏向一边；从最大音量降到静音时，一只耳机比另一只更晚无声。反复在最大和最小音量之间切换时，还可能出现音量跳变。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查思路 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先看手机下发音量后，主从耳是不是在 alarm 到点前就各自更新了本地媒体音量；再对比 alarm 回调里两边实际采用的目标值。&lt;/p&gt;
&lt;p&gt;已有问题中，主从耳在 alarm 到点前更新本地媒体音量的路径不同。快速滑动时，虽然两边在同一时刻进入 alarm 回调，但取到的音量基准已经不一致，所以会出现一边先变大、先变小或更晚静音。&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】ANC 模式是怎样切换的</title>
      <link>https://btio.cn/posts/2026/08/12/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFanc-%E6%A8%A1%E5%BC%8F%E6%98%AF%E6%80%8E%E6%A0%B7%E5%88%87%E6%8D%A2%E7%9A%84/</link>
      <pubDate>Wed, 12 Aug 2026 10:34:45 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/12/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFanc-%E6%A8%A1%E5%BC%8F%E6%98%AF%E6%80%8E%E6%A0%B7%E5%88%87%E6%8D%A2%E7%9A%84/</guid>
      <description>&lt;p&gt;最开始看原厂 SDK 时，我觉得它的切换很简单：确定关闭、降噪或通透，再调用模式接口即可。&lt;/p&gt;
&lt;p&gt;随着 App 控制、按键配置、单双耳记忆和出入盒切换逐渐加进来，我才发现应用层维护的并不只是一个 ANC 模式。提示音何时播放、左右耳怎样同步、状态什么时候保存和上报，也都在这条链路里。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、先把 ANC 的几条链路放在一起&lt;/h2&gt;
&lt;p&gt;ANC 模式可能因为三类事件发生变化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;App 下发一个明确的目标模式；&lt;/li&gt;
&lt;li&gt;用户通过耳机按键切换模式；&lt;/li&gt;
&lt;li&gt;耳机出入盒或 TWS 连接状态变化，需要重新选择单双耳记忆。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把三个入口放在一起，我目前是这样理解的：&lt;/p&gt;
&lt;div class=&#34;mermaid-diagram&#34; role=&#34;group&#34; aria-label=&#34;流程图&#34;&gt;
  &lt;pre class=&#34;mermaid&#34;&gt;%%{init: {&amp;#34;flowchart&amp;#34;: {&amp;#34;htmlLabels&amp;#34;: true, &amp;#34;wrappingWidth&amp;#34;: 1000, &amp;#34;rankSpacing&amp;#34;: 30}, &amp;#34;themeVariables&amp;#34;: {&amp;#34;lineColor&amp;#34;: &amp;#34;#71829a&amp;#34;}}}%%
flowchart TB
    subgraph R0[&amp;#34;① 确定目标模式&amp;#34;]
        direction LR
        R0N0[&amp;#34;收到 ANC 相关事件&amp;lt;br/&amp;gt;App 指令、按键事件&amp;lt;br/&amp;gt;出入盒或 TWS 状态变化&amp;#34;]
        R0N1[&amp;#34;确定当前使用场景&amp;lt;br/&amp;gt;单耳、双耳&amp;lt;br/&amp;gt;或者双耳都在盒内&amp;#34;]
        R0N2[&amp;#34;得到本次目标模式&amp;lt;br/&amp;gt;关闭、通透、不同等级的降噪&amp;lt;br/&amp;gt;或自适应降噪&amp;#34;]
        R0N0 --&amp;gt; R0N1 --&amp;gt; R0N2
    end
    class R0N0,R0N1,R0N2 blue;
    style R0 fill:#f1f6fd,stroke:#b8cfe8,color:#23456b
    subgraph R1[&amp;#34;② 同步并应用&amp;#34;]
        direction LR
        R1N0[&amp;#34;更新并同步状态&amp;lt;br/&amp;gt;选择或更新单耳、双耳记忆&amp;lt;br/&amp;gt;必要时通知对耳&amp;#34;]
        R1N1[&amp;#34;处理提示音并应用模式&amp;lt;br/&amp;gt;按触发来源处理提示音&amp;lt;br/&amp;gt;再转换成 SDK 模式&amp;#34;]
        R1N2[&amp;#34;保存并更新 App&amp;lt;br/&amp;gt;写入记忆；按键或状态变化时&amp;lt;br/&amp;gt;更新 App 显示&amp;#34;]
        R1N0 --&amp;gt; R1N1 --&amp;gt; R1N2
    end
    class R1N0,R1N1,R1N2 green;
    style R1 fill:#f0f8f3,stroke:#b9d7c4,color:#24563a
    R0 --&amp;gt; R1
    classDef blue fill:#e8f2ff,stroke:#7ba4d8,color:#23456b,stroke-width:1.5px;
    classDef green fill:#e7f4ec,stroke:#78aa8b,color:#24563a,stroke-width:1.5px;
    classDef amber fill:#fff3df,stroke:#d5a25b,color:#72501e,stroke-width:1.5px;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;这是一条方便理解的主线。实际代码里，App、按键和状态变化的处理顺序并不完全相同，后面再分别展开。&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】TWS 耳机 ANC 问题单复盘</title>
      <link>https://btio.cn/posts/2026/08/12/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA-anc-%E9%97%AE%E9%A2%98%E5%8D%95%E5%A4%8D%E7%9B%98/</link>
      <pubDate>Wed, 12 Aug 2026 00:00:00 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/12/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA-anc-%E9%97%AE%E9%A2%98%E5%8D%95%E5%A4%8D%E7%9B%98/</guid>
      <description>&lt;h2 id=&#34;preface&#34;&gt;写在开始&lt;/h2&gt;
&lt;p&gt;这篇文章整理项目中比较有代表性的 ANC 问题单，并按问题现象归为五类。&lt;/p&gt;
&lt;p&gt;有些问题标题虽然带有 ANC，但最后确认是电量、功耗或连接问题，这里不展开。本文只保留能够放回 ANC 控制链路解释的问题，没有明确结论的地方也不补充猜测。&lt;/p&gt;
&lt;p&gt;先把 ANC 控制流程简化成四步：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1. 收到切换请求
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2. 确定并同步目标模式
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3. 加载参数，更新实际效果
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4. 保存状态并上报 App
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本文围绕下面五类问题，整理对应的问题现象和排查思路：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;#section-1&#34;&gt;实际模式正常，App 显示却不对&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-2&#34;&gt;单耳和双耳切换后，ANC 状态混乱&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-3&#34;&gt;按键或 App 操作后，ANC 没有切换&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-4&#34;&gt;模式切换成功，降噪效果却不对&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-5&#34;&gt;切换 ANC 时出现杂音、短暂无声或突变&lt;/a&gt;
。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、实际模式正常，App 显示却不对&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;从耳长按切换 ANC 后，耳机实际已经切换，App 仍显示原来的模式；&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查思路 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先确认三个结果：从耳是否识别到长按、左右耳实际模式是否变化、主耳是否向 App 上报了新状态。&lt;/p&gt;
&lt;p&gt;如果从耳和两耳实际模式都正常，重点查主耳是否收到新的目标模式、更新本地状态并上报 App。问题单中，从耳已经完成 ANC 切换，但主耳没有更新状态并发送新模式，因此问题出在主耳的状态同步和上报链路。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题现象 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;耳机实际是高降噪，App 却显示低降噪；重新进入 App 后，等级还会发生跳变。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查思路 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;对照耳机内部等级、协议值和 App 显示值是否一致。问题单中，耳机使用 &lt;code&gt;1&lt;/code&gt; 表示低降噪、&lt;code&gt;3&lt;/code&gt; 表示高降噪，App 却映射成1是高降噪，3是低降噪，导致高低等级颠倒。这是状态映射错误，不是 ANC 参数没有生效。&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】TWS 耳机提示音问题单复盘</title>
      <link>https://btio.cn/posts/2026/08/11/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA%E6%8F%90%E7%A4%BA%E9%9F%B3%E9%97%AE%E9%A2%98%E5%8D%95%E5%A4%8D%E7%9B%98/</link>
      <pubDate>Tue, 11 Aug 2026 00:00:00 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/11/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA%E6%8F%90%E7%A4%BA%E9%9F%B3%E9%97%AE%E9%A2%98%E5%8D%95%E5%A4%8D%E7%9B%98/</guid>
      <description>&lt;h2 id=&#34;section-1&#34;&gt;一、本文要解决哪些提示音问题&lt;/h2&gt;
&lt;p&gt;这篇文章记录的是我对 TWS 耳机提示音问题的整理和理解。我结合问题单、原厂 SDK 以及实际修改点，把零散的问题按现象和根因重新归类，并从业务请求、队列、播放到状态恢复，整理出一套排查思路。&lt;/p&gt;
&lt;p&gt;具体问题的修改结论，以问题单和现有代码记录为准；没有明确依据的地方，我不额外延伸。理解提示音问题，可以先把流程分成四步：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;1. 业务事件发生
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;2. 提示音请求进入队列
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;3. 提示音开始播放
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;       ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;4. 播放结束，恢复音频状态
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;有些提示音只是给用户提供声音反馈，有些提示音还和业务回调绑定在一起。原厂 SDK 中，ANC 和音乐/游戏模式会在提示音播放完成后更新；接听、拒接和回拨也会在提示音相关回调中触发。&lt;/p&gt;
&lt;p&gt;所以排查时不能只看“有没有声音”，还要看对应的业务回调是否按预期执行。遇到问题时，先分清是“声音没有正常播放”，还是“提示音对应的业务动作没有执行”：前者检查资源、音量和播放链路，后者检查业务请求和回调。&lt;/p&gt;
&lt;p&gt;本文主要围绕以下 7 类问题展开，后文会分别说明问题现象、原因和排查思路：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;#section-2&#34;&gt;业务已经发生，提示音却没响或播错&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-3&#34;&gt;提示音太大、太小，或换资源后异常&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-4&#34;&gt;连续或快速操作后，提示音消失、滞后或重复&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-5&#34;&gt;提示音响错耳朵&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-6&#34;&gt;双耳提示音不同步，或音乐恢复不同步&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-7&#34;&gt;提示音响完，音乐却出现杂音或无声&lt;/a&gt;
；&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#section-8&#34;&gt;来电和双连接时，响铃状态混乱&lt;/a&gt;
。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、业务已经发生，提示音却没响或播错&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;电量已经低于 10%，却没有低电提示音。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查思路 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先确认低电条件是否真的触发。比如电量从 11% 直接变成 9% 时，可能跳过“等于 10%”的判断，后面就不会提交低电提示音。确认条件后，再检查代码是否走到提示音调用位置。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;问题现象 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;来电双击已经接听，却播放成三击提示音。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查思路 2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个问题说明提示音请求已经提交，但事件和提示音没有正确对应。重点检查双击事件最终传入的提示音资源编号，确认是否误用了三击提示音。&lt;/p&gt;
&lt;p&gt;这类问题可以按“业务条件、提示音调用、资源编号”的顺序检查。前三步没有问题后，再进入队列和播放流程。&lt;/p&gt;
&lt;h2 id=&#34;section-3&#34;&gt;三、提示音太大、太小，或换资源后异常&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;问题现象 1&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通过 App 的“查找耳机”功能播放正常，但在手机设置中的 Google“查找设备”页面，点击“让耳机响铃”时声音偏小。&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】TWS 耳机提示音是怎样播放出来的</title>
      <link>https://btio.cn/posts/2026/08/10/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA%E6%8F%90%E7%A4%BA%E9%9F%B3%E6%98%AF%E6%80%8E%E6%A0%B7%E6%92%AD%E6%94%BE%E5%87%BA%E6%9D%A5%E7%9A%84/</link>
      <pubDate>Mon, 10 Aug 2026 15:06:42 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/10/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA%E6%8F%90%E7%A4%BA%E9%9F%B3%E6%98%AF%E6%80%8E%E6%A0%B7%E6%92%AD%E6%94%BE%E5%87%BA%E6%9D%A5%E7%9A%84/</guid>
      <description>&lt;p&gt;在 TWS 耳机里，开机、蓝牙连接、低电量和降噪模式切换，通常都会伴随一段提示音。&lt;/p&gt;
&lt;p&gt;从表面看，提示音像是准备一个音频文件，需要时调用播放函数即可。实际上，一段提示音从代码触发到左右耳真正出声，中间还要经过资源查找、播放排队、双耳同步和音频恢复。&lt;/p&gt;
&lt;p&gt;本文以中科蓝讯 BT891x 原厂 SDK 的实现为例，重点分析提示音播放的核心流程。不同 SDK 的接口名称可能不一样，但整体思路通常可以互相参考。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、提示音播放的完整链路&lt;/h2&gt;
&lt;p&gt;一段提示音的完整流程，可以先简化成下面五步：&lt;/p&gt;
&lt;div class=&#34;mermaid-diagram&#34; role=&#34;group&#34; aria-label=&#34;流程图&#34;&gt;
  &lt;pre class=&#34;mermaid&#34;&gt;%%{init: {&amp;#34;flowchart&amp;#34;: {&amp;#34;htmlLabels&amp;#34;: true, &amp;#34;wrappingWidth&amp;#34;: 1000, &amp;#34;rankSpacing&amp;#34;: 30}, &amp;#34;themeVariables&amp;#34;: {&amp;#34;lineColor&amp;#34;: &amp;#34;#71829a&amp;#34;}}}%%
flowchart TB
    subgraph R0[&amp;#34;① 准备播放请求&amp;#34;]
        direction LR
        R0N0[&amp;#34;业务触发&amp;#34;]
        R0N1[&amp;#34;找到对应的提示音资源&amp;#34;]
        R0N2[&amp;#34;把播放请求加入队列&amp;#34;]
        R0N0 --&amp;gt; R0N1 --&amp;gt; R0N2
    end
    class R0N0,R0N1,R0N2 blue;
    style R0 fill:#f1f6fd,stroke:#b8cfe8,color:#23456b
    subgraph R1[&amp;#34;② 同步与播放&amp;#34;]
        direction LR
        R1N0[&amp;#34;确定哪只耳机播放&amp;lt;br/&amp;gt;并完成双耳同步&amp;#34;]
        R1N1[&amp;#34;解码播放&amp;lt;br/&amp;gt;结束后恢复原来的音频状态&amp;#34;]
        R1N0 --&amp;gt; R1N1
    end
    class R1N0,R1N1 green;
    style R1 fill:#f0f8f3,stroke:#b9d7c4,color:#24563a
    R0 --&amp;gt; R1
    classDef blue fill:#e8f2ff,stroke:#7ba4d8,color:#23456b,stroke-width:1.5px;
    classDef green fill:#e7f4ec,stroke:#78aa8b,color:#24563a,stroke-width:1.5px;
    classDef amber fill:#fff3df,stroke:#d5a25b,color:#72501e,stroke-width:1.5px;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;比如手机连接耳机成功以后，蓝牙业务只需要提出“播放连接成功提示音”这个请求。至于提示音文件放在哪里、左右耳什么时候开始播放、当前音乐要不要降低音量，都会继续交给提示音模块处理。&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】TWS 按键问题单复盘</title>
      <link>https://btio.cn/posts/2026/08/07/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E6%8C%89%E9%94%AE%E9%97%AE%E9%A2%98%E5%8D%95%E5%A4%8D%E7%9B%98/</link>
      <pubDate>Fri, 07 Aug 2026 20:17:54 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/07/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E6%8C%89%E9%94%AE%E9%97%AE%E9%A2%98%E5%8D%95%E5%A4%8D%E7%9B%98/</guid>
      <description>&lt;h2 id=&#34;section-1&#34;&gt;一、写在开始&lt;/h2&gt;
&lt;p&gt;我参与的是一款基于中科蓝讯 BT8910 的 TWS 耳机品牌项目，目前这个项目已经完成。&lt;/p&gt;
&lt;p&gt;项目结束后，我想把开发过程中接触到的 Jira 问题单重新梳理一遍。其中既有我参与处理的内容，也有同事负责的模块。&lt;/p&gt;
&lt;p&gt;现在查一个函数、一个配置项并不难，真正值得记录的是：面对一个问题现象时，应该先判断哪一层，为什么往这个方向查，以及怎么通过日志一步步缩小范围。&lt;/p&gt;
&lt;p&gt;所以这次复盘不是给出一套标准答案，而是站在我目前对应用层的理解上，把问题现象、判断过程和排查思路整理下来，供遇到类似问题的同学参考。&lt;/p&gt;
&lt;p&gt;整理过程中我还发现，很多反馈看起来不一样，根因却有重复。按根因归类后，下次遇到类似现象时，就可以先判断问题属于哪个环节，再沿对应链路往下查。&lt;/p&gt;
&lt;p&gt;后续我会继续按模块整理项目中的问题单，包括按键、提示音、机盒通信、ANC、音量控制、功耗管理、出入盒、双耳同步、蓝牙问题和连接问题等内容。这一篇先从按键开始，归纳其中比较常见的 4 类问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;阅读提示&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;全文约 2 300 字，预计 10 分钟读完。&lt;/p&gt;
&lt;p&gt;本文包含 4 类按键问题：完全无响应、场景功能错误、误触，以及复杂手势互相干扰。重点不是讲基础原理，而是遇到类似反馈时，应该先查哪里、再往哪里查。&lt;/p&gt;
&lt;p&gt;本文只讨论按键链路本身。如果按键已经触发正确功能，只是提示音没有播放或播放不对，就归到后续的提示音问题中。&lt;/p&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、所有手势都没有反应&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;客户反馈&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;其中一只耳机的触控按键完全无效，双击、三击、长按都没有反应。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查思路&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步：确认触控输入&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先看这只耳机的触控按键是否有按下、松开的日志。&lt;/p&gt;
&lt;p&gt;没有日志：先查这个触控按键的输入本身，例如触控是否启用、耳机是否在盒内、是否正处于禁键时间、触控阈值或通道是否正常。&lt;/p&gt;
&lt;p&gt;有日志：说明这个触控按键已经有输入，继续往下查。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二步：确认手势识别&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;再看目标手势有没有识别出来。&lt;/p&gt;
&lt;p&gt;没有识别出来：重点查双击间隔、长按时间、松手后状态是否复位，以及组合手势是否把普通手势挡住了。&lt;/p&gt;
&lt;p&gt;识别出来：说明手势已经产生，继续看功能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三步：确认功能消息&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;最后看手势有没有转换成正确的功能消息。&lt;/p&gt;
&lt;p&gt;没有、为空或错误：查按键定义、场景切换和配置更新。&lt;/p&gt;
&lt;p&gt;有正确消息：再确认功能处理是否执行；如果功能已经执行，后面只是没有提示音，就属于提示音问题，不再按按键问题处理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;判断与处理&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;“完全无效”不一定是触控硬件问题，也可能是手势已经产生、但后面没有正确链接到功能。处理时不要一开始就调灵敏度或改功能定义，先通过日志确认问题停在哪一层。&lt;/p&gt;
&lt;h2 id=&#34;section-3&#34;&gt;三、同一手势，功能却错了&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;客户反馈&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;听歌时双击正常，来电时双击却不能接听或挂断。&lt;/li&gt;
&lt;li&gt;长按在听歌时能切换功能，到响铃或通话时却失效。&lt;/li&gt;
&lt;li&gt;App 改完触控后，耳机执行的功能不对。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;排查思路&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步：固定一个场景复现&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;只测听歌、只测来电响铃，或只测通话，避免混在一起判断。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二步：确认手势已经识别&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果手势都没出来，回到第一类问题排查。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三步：核对当前场景的功能&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;手势正常后，直接看耳机最后想执行的是什么功能。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同样是“双击”：听歌时可能是切歌，来电时应该是接听或拒接，通话中又可能是其他操作。&lt;/li&gt;
&lt;li&gt;来电时双击却去切歌：说明双击识别没有问题，只是耳机还在按“听歌时的规则”处理。&lt;/li&gt;
&lt;li&gt;只有通话异常：重点检查进入通话后，双击和长按是否已经改成通话时该做的功能。&lt;/li&gt;
&lt;li&gt;App 改完才异常：重点检查新设置是否把听歌、来电或通话时的功能改错了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;判断与处理&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;听歌、来电响铃、通话时都可以识别同一个“双击”或“长按”，但后续要执行的功能并不相同。&lt;/p&gt;
&lt;p&gt;这类问题通常是耳机没有按当前使用状态执行对应功能。处理重点是把每种状态下的功能对应关系确认清楚。&lt;/p&gt;
&lt;h2 id=&#34;section-4&#34;&gt;四、不该响应时仍然响应&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;客户反馈&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;耳机放在盒内还能切歌；刚出盒或拿取耳机时，容易误触切换功能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查思路&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步：确认误触发生的时机&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】TWS 耳机按键处理流程</title>
      <link>https://btio.cn/posts/2026/08/07/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA%E6%8C%89%E9%94%AE%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B/</link>
      <pubDate>Fri, 07 Aug 2026 18:39:14 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/07/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFtws-%E8%80%B3%E6%9C%BA%E6%8C%89%E9%94%AE%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B/</guid>
      <description>&lt;p&gt;上一篇已经把 SDK 的几个核心概念过了一遍，包括模式状态机、process/message/display 和消息分层。&lt;/p&gt;
&lt;p&gt;这一篇就拿按键功能做一次具体分析，从一个按键事件出发，一直追到最终执行的位置。&lt;/p&gt;
&lt;p&gt;以我接触到的 TWS 项目来看，按键是应用层里相对好理解、也比较容易上手的一部分：改单击功能、加长按、加多击，或者调整不同模式下的按键行为。这些需求在 SDK 里都对应到同一套机制，弄懂之后，定位修改位置会更快。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、先跑一遍完整链路&lt;/h2&gt;
&lt;p&gt;一个按键从手指按下到功能执行，在 SDK 里走了这几步：&lt;/p&gt;
&lt;div class=&#34;mermaid-diagram&#34; role=&#34;group&#34; aria-label=&#34;流程图&#34;&gt;
  &lt;pre class=&#34;mermaid&#34;&gt;%%{init: {&amp;#34;flowchart&amp;#34;: {&amp;#34;htmlLabels&amp;#34;: true, &amp;#34;wrappingWidth&amp;#34;: 1000, &amp;#34;rankSpacing&amp;#34;: 30}, &amp;#34;themeVariables&amp;#34;: {&amp;#34;lineColor&amp;#34;: &amp;#34;#71829a&amp;#34;}}}%%
flowchart TB
    subgraph R0[&amp;#34;① 输入与识别&amp;#34;]
        direction LR
        R0N0[&amp;#34;手指按下&amp;lt;br/&amp;gt;用户触发按键&amp;#34;]
        R0N1[&amp;#34;硬件扫描&amp;lt;br/&amp;gt;读取按键编号&amp;#34;]
        R0N2[&amp;#34;消抖 / 识别&amp;lt;br/&amp;gt;判断短按、长按、多击&amp;#34;]
        R0N0 --&amp;gt; R0N1 --&amp;gt; R0N2
    end
    class R0N0,R0N1,R0N2 blue;
    style R0 fill:#f1f6fd,stroke:#b8cfe8,color:#23456b
    subgraph R1[&amp;#34;② 消息与业务&amp;#34;]
        direction LR
        R1N0[&amp;#34;查按键表&amp;lt;br/&amp;gt;找到对应消息&amp;#34;]
        R1N1[&amp;#34;消息入队&amp;lt;br/&amp;gt;放入消息队列&amp;#34;]
        R1N2[&amp;#34;模式分发&amp;lt;br/&amp;gt;按当前模式处理&amp;#34;]
        R1N3[&amp;#34;功能执行&amp;lt;br/&amp;gt;播放、切歌、接听等&amp;#34;]
        R1N0 --&amp;gt; R1N1 --&amp;gt; R1N2 --&amp;gt; R1N3
    end
    class R1N0,R1N1,R1N2,R1N3 green;
    style R1 fill:#f0f8f3,stroke:#b9d7c4,color:#24563a
    R0 --&amp;gt; R1
    classDef blue fill:#e8f2ff,stroke:#7ba4d8,color:#23456b,stroke-width:1.5px;
    classDef green fill:#e7f4ec,stroke:#78aa8b,color:#24563a,stroke-width:1.5px;
    classDef amber fill:#fff3df,stroke:#d5a25b,color:#72501e,stroke-width:1.5px;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;前两步主要回答“这个按键物理上发生了什么”。从第 3 步开始，软件才把按键行为转换成功能。&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】聊聊我对这套SDK的理解</title>
      <link>https://btio.cn/posts/2026/08/06/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF%E8%81%8A%E8%81%8A%E6%88%91%E5%AF%B9%E8%BF%99%E5%A5%97sdk%E7%9A%84%E7%90%86%E8%A7%A3/</link>
      <pubDate>Thu, 06 Aug 2026 19:59:39 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/06/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF%E8%81%8A%E8%81%8A%E6%88%91%E5%AF%B9%E8%BF%99%E5%A5%97sdk%E7%9A%84%E7%90%86%E8%A7%A3/</guid>
      <description>&lt;p&gt;前两篇记录了环境搭建和 com/bank 内存的事。这篇主要想讲一下中科蓝汛的 SDK——不讲具体细节，也不列目录结构，每个项目的目录可能都不太一样，列了也没太大意义。主要是聊一下我对这套 SDK 几个核心概念的理解。以后不管拿到哪个版本、做什么功能，这几个概念应该都不会变。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、SDK 就是一个&amp;quot;模式状态机&amp;quot;&lt;/h2&gt;
&lt;p&gt;这是做完项目之后最大的感受。一颗 TWS 耳机芯片，任何时候只处于一个&amp;quot;模式&amp;quot;——蓝牙模式、充电模式、IDLE 待机、关机。切模式就是改一个变量 &lt;code&gt;func_cb.sta&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// func_run() —— 整个 SDK 的主循环就长这样
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;func_enter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;func_cb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sta&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;FUNC_BT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;      &lt;span class=&#34;nf&#34;&gt;func_bt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;      &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 蓝牙
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;FUNC_CHARGE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;nf&#34;&gt;func_charge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 充电
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;FUNC_IDLE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;func_idle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 待机
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nl&#34;&gt;FUNC_PWROFF&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;nf&#34;&gt;func_pwroff&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 关机
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;           &lt;span class=&#34;nf&#34;&gt;func_exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 退出，切下一个
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;虽然每个模式做的事情不一样，但内部结构都有共同点。拿蓝牙模式看：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;func_bt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;func_bt_enter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;                    &lt;span class=&#34;c1&#34;&gt;// 进来先初始化
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;func_cb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sta&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FUNC_BT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 没切模式就一直在这转
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;func_bt_process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;              &lt;span class=&#34;c1&#34;&gt;// 轮询事件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;func_bt_message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;msg_dequeue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 处理消息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nf&#34;&gt;func_bt_display&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;              &lt;span class=&#34;c1&#34;&gt;// 刷新显示
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nf&#34;&gt;func_bt_exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;                     &lt;span class=&#34;c1&#34;&gt;// 走之前收尾
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;就是 enter → while → process/message/display → exit。再看 idle 模式，几乎一模一样——&lt;code&gt;func_idle_enter()&lt;/code&gt; → &lt;code&gt;while(func_cb.sta == FUNC_IDLE)&lt;/code&gt; → &lt;code&gt;process/message/display&lt;/code&gt; → 退出。充电和关机模式虽然内部细节不同，但也是 enter → 做事 → exit 这个三段式。&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】从两次偶发死机，理解 com 区和 bank 区</title>
      <link>https://btio.cn/posts/2026/08/06/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF%E4%BB%8E%E4%B8%A4%E6%AC%A1%E5%81%B6%E5%8F%91%E6%AD%BB%E6%9C%BA%E7%90%86%E8%A7%A3-com-%E5%8C%BA%E5%92%8C-bank-%E5%8C%BA/</link>
      <pubDate>Thu, 06 Aug 2026 08:00:00 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/06/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AF%E4%BB%8E%E4%B8%A4%E6%AC%A1%E5%81%B6%E5%8F%91%E6%AD%BB%E6%9C%BA%E7%90%86%E8%A7%A3-com-%E5%8C%BA%E5%92%8C-bank-%E5%8C%BA/</guid>
      <description>&lt;p&gt;刚接触中科蓝讯 SDK 时，我经常看到函数前面有 &lt;code&gt;AT(.com_text.xxx)&lt;/code&gt;。一开始我只知道照着原有代码写，却不明白它为什么在这里。后来看了资料和工程里的写法，我才认识到：SDK 把代码分区域存放，不同区域的运行方式不一样。&lt;/p&gt;
&lt;p&gt;这篇只是我目前从应用侧整理的一点理解，重点放在看到 &lt;code&gt;AT(...)&lt;/code&gt; 时怎么读。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、com 区和 bank 区有什么区别&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&#34;com 区和 bank 区的存放与运行示意&#34; loading=&#34;lazy&#34; src=&#34;https://btio.cn/images/csdn/163511359/image.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;按照官方资料的说明，芯片上电后，一部分代码会从 Flash 搬到 RAM 里常驻下来，这部分就是 &lt;code&gt;com&lt;/code&gt; 区；剩下的代码继续待在 Flash，用到时才被加载到 &lt;code&gt;bank&lt;/code&gt; 运行区。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;区域&lt;/th&gt;
					&lt;th&gt;简单理解&lt;/th&gt;
					&lt;th&gt;适合放什么&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;com&lt;/code&gt; 区&lt;/td&gt;
					&lt;td&gt;上电后会加载到 RAM，并在运行期间常驻&lt;/td&gt;
					&lt;td&gt;中断函数、实时性要求高的&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;bank&lt;/code&gt; 区&lt;/td&gt;
					&lt;td&gt;代码存在 Flash，运行时按需加载到 bank 运行区&lt;/td&gt;
					&lt;td&gt;大多数普通应用逻辑&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;com&lt;/code&gt; 区的空间有限，所以不要因为更快，就把普通功能都放进去。&lt;/p&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、真正要注意的是中断和它调用的函数&lt;/h2&gt;
&lt;p&gt;中断函数和它调到的关键函数，通常需要放在 &lt;code&gt;com&lt;/code&gt; 区。因为中断来了要马上处理，不能等到需要时再去加载代码。&lt;/p&gt;
&lt;p&gt;在工程里看定时和一些中断相关代码时，我能看到类似下面的写法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nf&#34;&gt;AT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;com_text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;user_timer_isr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 中断中的处理
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;实际用的时候，我是先看同模块已有代码的写法，不凭空猜。但照抄还不够——踩过两次坑才知道，光盯着函数本身的 &lt;code&gt;AT&lt;/code&gt;，容易漏掉更深层的依赖。&lt;/p&gt;
&lt;p&gt;第一次是在中断里调了一个普通函数，我给那个函数加了 &lt;code&gt;AT(.com_text.xxx)&lt;/code&gt;，以为这就完事了。结果还是偶发异常。排查后发现，这个函数内部又调了另一个普通函数，那个子函数没加 &lt;code&gt;AT&lt;/code&gt;。中断触发时 CPU 跳到 com 区，执行到嵌套的子函数时，它的代码还在 bank 区没加载进来，一样挂。这件事的风险在于，被调函数可能还调了其他函数，嵌套几层之后很容易漏掉。所以给函数加 &lt;code&gt;AT&lt;/code&gt; 时，得顺着调用链往下检查，确保整条执行路径上的函数都在 com 区。如果调用深度比较大，更稳妥的做法是通过消息转发把逻辑放到其他线程执行，避免在 com 区里越套越深。&lt;/p&gt;</description>
    </item>
    <item>
      <title>【中科蓝讯】SDK 环境搭建与烧录</title>
      <link>https://btio.cn/posts/2026/08/05/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFsdk-%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%B8%8E%E7%83%A7%E5%BD%95/</link>
      <pubDate>Wed, 05 Aug 2026 14:16:23 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/05/%E4%B8%AD%E7%A7%91%E8%93%9D%E8%AE%AFsdk-%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%B8%8E%E7%83%A7%E5%BD%95/</guid>
      <description>&lt;p&gt;以我最近实际接触的中科蓝讯 BT891x 项目为例，刚开始最先要做的就是把开发环境搭起来：准备资料和工具、打开工程、编译生成 &lt;code&gt;app.dcf&lt;/code&gt;，再用 Downloader 完成后续烧录。&lt;/p&gt;
&lt;p&gt;不同 SDK 版本和开发板的目录、工具版本可能不一样，最终以当前资料包和工程说明为准。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、准备资料和工具&lt;/h2&gt;
&lt;p&gt;如果手上的 SDK 对应特定的 TWS 开发板，原理图和相关硬件资料也要一并保存好，后面核对硬件配置、下载和外设问题时都会用到。&lt;/p&gt;
&lt;p&gt;刚开始做 BT891x 时，主要先准备下面四项资料和工具：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;内容&lt;/th&gt;
					&lt;th&gt;先拿它做什么&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;BT891x 固件 SDK&lt;/td&gt;
					&lt;td&gt;后续的功能开发主要就在这里进行&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CodeBlocks 开发环境&lt;/td&gt;
					&lt;td&gt;打开 &lt;code&gt;.cbp&lt;/code&gt; 工程并发起编译&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;RV32 Toolchain&lt;/td&gt;
					&lt;td&gt;供 CodeBlocks 调用的 RISC-V 编译工具链&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Downloader&lt;/td&gt;
					&lt;td&gt;读取 &lt;code&gt;app.dcf&lt;/code&gt;，进行配置和烧录处理&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;如果当前使用的是串口下载方式，还要保留蓝讯提供的串口驱动。资料包里的驱动通常以 CP210x 驱动包的形式提供，用于让电脑识别 CP2102 或 XLink 模块对应的 COM 口。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;BT891x 工具目录与安装、编译、烧录流程&#34; loading=&#34;lazy&#34; src=&#34;https://btio.cn/images/csdn/163496844/2e1ff8f206554d969a4660b56c737383.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;CodeBlocks 和 RV32 Toolchain 都是双击安装程序后，按提示一路选择 &lt;code&gt;Next&lt;/code&gt;、默认路径安装即可。CodeBlocks 用来打开 &lt;code&gt;.cbp&lt;/code&gt; 工程并编译，RV32 Toolchain 是真正执行编译的工具。&lt;/p&gt;
&lt;p&gt;如果使用串口下载，按当前下载器对应的驱动安装即可。下载器和设备接入电脑后，系统能够识别出 COM 口，Downloader 才能继续扫描设备。&lt;/p&gt;
&lt;p&gt;Downloader 不需要安装，解压后直接运行 &lt;code&gt;Downloader.exe&lt;/code&gt; 即可。&lt;/p&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、编译与烧录&lt;/h2&gt;
&lt;p&gt;环境装完后，我不会马上改功能，而是先在 CodeBlocks 中打开 SDK 里的 &lt;code&gt;.cbp&lt;/code&gt; 工程并编译一次。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
