<?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/%E8%93%9D%E7%89%99%E8%80%B3%E6%9C%BA%E5%BC%80%E5%8F%91/</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>Tue, 04 Aug 2026 17:48:36 +0800</lastBuildDate>
    <atom:link href="https://btio.cn/categories/%E8%93%9D%E7%89%99%E8%80%B3%E6%9C%BA%E5%BC%80%E5%8F%91/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>一副 TWS 耳机，为什么既要用经典蓝牙，又要用 BLE</title>
      <link>https://btio.cn/posts/2026/08/04/%E4%B8%80%E5%89%AF-tws-%E8%80%B3%E6%9C%BA%E4%B8%BA%E4%BB%80%E4%B9%88%E6%97%A2%E8%A6%81%E7%94%A8%E7%BB%8F%E5%85%B8%E8%93%9D%E7%89%99%E5%8F%88%E8%A6%81%E7%94%A8-ble/</link>
      <pubDate>Tue, 04 Aug 2026 17:48:36 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/04/%E4%B8%80%E5%89%AF-tws-%E8%80%B3%E6%9C%BA%E4%B8%BA%E4%BB%80%E4%B9%88%E6%97%A2%E8%A6%81%E7%94%A8%E7%BB%8F%E5%85%B8%E8%93%9D%E7%89%99%E5%8F%88%E8%A6%81%E7%94%A8-ble/</guid>
      <description>&lt;p&gt;做 TWS 耳机时，经常会同时看到经典蓝牙和 BLE。&lt;/p&gt;
&lt;p&gt;手机已经通过蓝牙连接耳机，可以播放音乐、接打电话，为什么配套 App 还要再连接一次 BLE？我刚开始接触时，也没有认真区分这两部分，只知道它们都叫蓝牙。&lt;/p&gt;
&lt;p&gt;后来做 App 交互功能多了，我才慢慢理解：在目前常见的 TWS 产品里，它们处理的事情并不一样。经典蓝牙主要负责音乐、通话和媒体控制，BLE 更像一条给 App 使用的数据通道。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、经典蓝牙主要负责耳机的基本功能&lt;/h2&gt;
&lt;p&gt;平时用耳机听歌、调节音量和接打电话，主要涉及经典蓝牙中的几个 Profile：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Profile&lt;/th&gt;
					&lt;th&gt;在耳机中的主要作用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;A2DP&lt;/td&gt;
					&lt;td&gt;传输音乐音频&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;AVRCP&lt;/td&gt;
					&lt;td&gt;播放、暂停、切歌和音量控制&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;HFP&lt;/td&gt;
					&lt;td&gt;来电、接听、挂断等通话控制&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;通话时的语音通常还会通过 SCO/eSCO 链路传输。&lt;/p&gt;
&lt;p&gt;这些功能决定了一副耳机能不能正常听歌和通话。以我目前接触到的应用开发来说，平时主要处理 SDK 上报的 A2DP、AVRCP 和 HFP 事件，再根据当前状态执行对应的产品逻辑。&lt;/p&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、BLE 主要负责耳机与 App 交互&lt;/h2&gt;
&lt;p&gt;配套 App 需要做的事情和播放音乐不太一样。它传输的通常不是连续音频，而是一些控制命令和状态数据。&lt;/p&gt;
&lt;p&gt;我做过的项目里，BLE 主要用于这些功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;修改 EQ；&lt;/li&gt;
&lt;li&gt;切换 ANC 模式；&lt;/li&gt;
&lt;li&gt;读取耳机电量；&lt;/li&gt;
&lt;li&gt;OTA 升级；&lt;/li&gt;
&lt;li&gt;查找耳机。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如用户在 App 上切换 ANC，App 会通过 BLE 把命令发给耳机；耳机执行以后，还可能需要把最新状态返回给 App。读取电量也是类似的过程，只是数据方向和触发时机可能不同。&lt;/p&gt;
&lt;p&gt;我接触过 GATT 的 Service、Characteristic、读写和通知，不过实际开发时，大部分数据收发还是通过 SDK 封装好的接口完成。刚开始看这些名词时，我觉得它们比较抽象。结合 TWS 耳机的功能，可以先这样理解：&lt;/p&gt;</description>
    </item>
    <item>
      <title>做耳机开发，需要掌握哪些 C 语言知识</title>
      <link>https://btio.cn/posts/2026/08/03/%E5%81%9A%E8%80%B3%E6%9C%BA%E5%BC%80%E5%8F%91%E9%9C%80%E8%A6%81%E6%8E%8C%E6%8F%A1%E5%93%AA%E4%BA%9B-c-%E8%AF%AD%E8%A8%80%E7%9F%A5%E8%AF%86/</link>
      <pubDate>Mon, 03 Aug 2026 16:29:04 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/03/%E5%81%9A%E8%80%B3%E6%9C%BA%E5%BC%80%E5%8F%91%E9%9C%80%E8%A6%81%E6%8E%8C%E6%8F%A1%E5%93%AA%E4%BA%9B-c-%E8%AF%AD%E8%A8%80%E7%9F%A5%E8%AF%86/</guid>
      <description>&lt;p&gt;TWS 应用层的大部分工作，最后还是要落到 C 代码上。这篇整理开发中经常用到、也容易混淆的内容，方便需要时快速回顾。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、数据类型与类型转换&lt;/h2&gt;
&lt;p&gt;嵌入式代码经常使用 &lt;code&gt;uint8_t&lt;/code&gt;、&lt;code&gt;uint16_t&lt;/code&gt; 和 &lt;code&gt;uint32_t&lt;/code&gt;，因为它们的位宽是明确的。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;int&lt;/code&gt; 占多少字节，由芯片架构和编译器决定。在常见的 32 位平台上，&lt;code&gt;int&lt;/code&gt; 通常占 4 字节；在部分嵌入式平台上，也可能只占 2 字节。可以使用 &lt;code&gt;sizeof&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;nf&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;int size: %u&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&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;kt&#34;&gt;unsigned&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&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;sizeof&lt;/code&gt; 返回的是 &lt;code&gt;size_t&lt;/code&gt; 类型。这里先把结果转换成 &lt;code&gt;unsigned int&lt;/code&gt;，再使用 &lt;code&gt;%u&lt;/code&gt; 输出，适合常见的嵌入式 &lt;code&gt;printf&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;普通计算和循环计数可以使用 &lt;code&gt;int&lt;/code&gt;。通信协议、Flash 数据、文件格式和寄存器字段需要明确长度时，优先使用固定位宽类型。&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;高位可能被截断&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;tr&gt;
					&lt;td&gt;指针类型转换&lt;/td&gt;
					&lt;td&gt;只改变读取内存的方式，不会改变原来的数据&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：有符号数和无符号数比较前，尽量把两边统一成合适的类型。进行指针类型转换时，要确认原内存长度不小于目标类型的大小。例如原数据只有 1 字节，转换成 &lt;code&gt;uint32_t *&lt;/code&gt; 后却按照 4 字节读取，就会读到不属于它的内存。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、指针、数组与生命周期&lt;/h2&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;指针指向的数据类型是否正确。&lt;/li&gt;
&lt;/ul&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;update_state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint8_t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&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;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&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;return&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;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#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&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;sizeof&lt;/code&gt; 得到原数组长度。因此传递缓冲区时，最好同时传入长度。&lt;/p&gt;</description>
    </item>
    <item>
      <title>做 TWS 应用开发，到底需不需要懂协议栈</title>
      <link>https://btio.cn/posts/2026/08/03/%E5%81%9A-tws-%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E5%88%B0%E5%BA%95%E9%9C%80%E4%B8%8D%E9%9C%80%E8%A6%81%E6%87%82%E5%8D%8F%E8%AE%AE%E6%A0%88/</link>
      <pubDate>Mon, 03 Aug 2026 11:26:31 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/08/03/%E5%81%9A-tws-%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E5%88%B0%E5%BA%95%E9%9C%80%E4%B8%8D%E9%9C%80%E8%A6%81%E6%87%82%E5%8D%8F%E8%AE%AE%E6%A0%88/</guid>
      <description>&lt;p&gt;刚接触 TWS 应用开发时，我一直有一个疑问：蓝牙协议栈大部分已经由芯片原厂封装好了，平时做功能主要是调用 SDK 接口、处理事件和编写产品逻辑，还有没有必要专门学习协议栈？&lt;/p&gt;
&lt;p&gt;刚入行的时候，我也尝试过从头学习蓝牙协议。当时下载了一份 Bluetooth Core Specification 6.0，打开后看到整整 3815 页，心里压力一下就上来了。里面有大量英文术语、状态、命令和数据包格式，我硬着头皮看了几天，还是很难把规范里的内容和手上的代码对应起来，最后没有坚持下去。&lt;/p&gt;
&lt;p&gt;工作两年左右，我对协议栈内部仍然谈不上有多深入的理解，平时接触最多的还是应用层代码。所以我现在的看法是：**需要了解一些，但没必要一开始钻得特别深。**这篇文章只是记录我目前在项目中用到的一些认识。&lt;/p&gt;
&lt;h2 id=&#34;section-1&#34;&gt;一、我目前主要关注什么&lt;/h2&gt;
&lt;p&gt;以我目前的工作内容来说，HCI、L2CAP 以及各个 Profile 的内部状态机，我其实没有深入研究。日常开发更常见的是调用 SDK 接口、接收事件，再根据产品状态处理业务。&lt;/p&gt;
&lt;p&gt;为了不把不同问题混在一起，我会先记住几个和工作直接相关的区别：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ACL 连接和 Profile 连接不是一回事。&lt;/li&gt;
&lt;li&gt;A2DP 负责音乐，AVRCP 负责播放控制和音量同步。&lt;/li&gt;
&lt;li&gt;HFP 负责通话控制，SCO/eSCO 承载通话语音。&lt;/li&gt;
&lt;li&gt;SPP 和 BLE GATT 常用于 App 数据通信。&lt;/li&gt;
&lt;li&gt;接口调用成功，不一定代表异步操作已经完成，还要等待后续事件。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了更直观地区分这些连接和业务，可参考蓝牙连接关系简化图：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;手机与 TWS 耳机的蓝牙连接关系&#34; loading=&#34;lazy&#34; src=&#34;https://btio.cn/images/csdn/163436393/%E6%89%8B%E6%9C%BA%E4%B8%8ETWS%E8%80%B3%E6%9C%BA%E7%9A%84%E8%93%9D%E7%89%99%E8%BF%9E%E6%8E%A5%E5%85%B3%E7%B3%BB.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;图中把 GATT 单独放在 BLE 连接下。严格来说，协议内部的承载关系还要更细，这里只保留我在应用开发中经常接触到的部分。&lt;/p&gt;
&lt;p&gt;我目前也就是先把这些常见概念分清。这样遇到异常时，至少知道先看哪一类状态，不至于一上来就把所有现象都归结成“蓝牙有问题”。&lt;/p&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、实际项目里，我能接触到什么&lt;/h2&gt;
&lt;p&gt;以我使用的中科蓝讯 &lt;code&gt;bt8910&lt;/code&gt; SDK 为例，工程里可以看到 A2DP、HFP、SPP 和 TWS 相关的配置及适配代码，但真正的核心实现主要由预编译的 &lt;code&gt;libbtstack.a&lt;/code&gt; 提供。&lt;/p&gt;
&lt;p&gt;应用层这边，A2DP 和 HFP 主要是调整部分参数、接收事件和处理业务回调；SPP 的 SDP Record 和数据收发回调可以看到并修改；GATT 可以添加 Service、Characteristic 和读写回调；TWS 这边则能做一些业务状态同步和主从切换处理。&lt;/p&gt;</description>
    </item>
    <item>
      <title>一副 TWS 耳机里，有哪些软件模块</title>
      <link>https://btio.cn/posts/2026/07/31/%E4%B8%80%E5%89%AF-tws-%E8%80%B3%E6%9C%BA%E9%87%8C%E6%9C%89%E5%93%AA%E4%BA%9B%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97/</link>
      <pubDate>Fri, 31 Jul 2026 18:32:50 +0800</pubDate>
      <guid>https://btio.cn/posts/2026/07/31/%E4%B8%80%E5%89%AF-tws-%E8%80%B3%E6%9C%BA%E9%87%8C%E6%9C%89%E5%93%AA%E4%BA%9B%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97/</guid>
      <description>&lt;h2 id=&#34;section-1&#34;&gt;一、先看整体&lt;/h2&gt;
&lt;p&gt;如果暂时不考虑具体芯片平台，TWS 耳机的软件大致可以分成下面几个部分：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;TWS 耳机软件模块组成&#34; loading=&#34;lazy&#34; src=&#34;https://btio.cn/images/csdn/163373023/software-modules.svg&#34;&gt;&lt;/p&gt;
&lt;p&gt;这不是严格的分层架构。实际 SDK 中，各部分往往会互相调用，甚至放在同一个模块里。这样划分主要是为了看清楚：一个耳机功能背后，通常有哪些软件在配合。&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;系统与 RTOS&lt;/td&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;td&gt;GPIO、ADC、I2C、I2S、充电、休眠、唤醒&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;蓝牙连接&lt;/td&gt;
					&lt;td&gt;与手机建立和维护蓝牙业务&lt;/td&gt;
					&lt;td&gt;配对、回连、A2DP、AVRCP、HFP、BLE&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;双耳协同&lt;/td&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;td&gt;编解码、播放、录音、提示音、ANC、ENC&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;产品应用&lt;/td&gt;
					&lt;td&gt;决定耳机具体怎样工作&lt;/td&gt;
					&lt;td&gt;按键、LED、佩戴检测、状态机、功能优先级&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;数据与升级&lt;/td&gt;
					&lt;td&gt;保存配置并更新固件&lt;/td&gt;
					&lt;td&gt;配对记录、用户设置、OTA、版本管理&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;调试与测试&lt;/td&gt;
					&lt;td&gt;帮助发现和定位问题&lt;/td&gt;
					&lt;td&gt;日志、异常信息、测试模式、量产支持&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;section-2&#34;&gt;二、系统与 RTOS&lt;/h2&gt;
&lt;p&gt;TWS 耳机一般不是从上到下只执行一条固定流程。蓝牙事件、按键、音频、充电和定时任务可能在不同时间发生，因此软件需要同时管理多种事情。&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;软件定时器&lt;/li&gt;
&lt;li&gt;内存分配与任务栈&lt;/li&gt;
&lt;li&gt;中断处理&lt;/li&gt;
&lt;li&gt;看门狗和异常恢复&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;应用层开发中经常接触到的，并不是 RTOS 调度器本身，而是平台提供的任务、消息和回调接口。&lt;/p&gt;
&lt;p&gt;例如，按键驱动检测到一次双击后，不一定会直接执行“切换降噪模式”。它可能先产生按键事件，再把事件投递给应用任务，最后由应用代码根据当前状态决定是否执行这个功能。&lt;/p&gt;
&lt;p&gt;所以，当我们阅读一个新 SDK 时，找到事件入口和消息流转路径很重要。&lt;/p&gt;
&lt;h2 id=&#34;section-3&#34;&gt;三、硬件驱动与电源管理&lt;/h2&gt;
&lt;p&gt;耳机虽然体积不大，但里面仍然有不少硬件。软件需要通过不同接口读取状态或者控制这些器件。&lt;/p&gt;
&lt;p&gt;常见内容包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GPIO：NTC供电、LED、使能信号&lt;/li&gt;
&lt;li&gt;ADC：按键电压、其他模拟量&lt;/li&gt;
&lt;li&gt;UART：日志输出、机盒&lt;/li&gt;
&lt;li&gt;充电与盒仓检测：识别 DC、入盒、出盒、开盖、关盖、充电和满电等状态&lt;/li&gt;
&lt;li&gt;佩戴检测：红外、触摸或其他传感器&lt;/li&gt;
&lt;li&gt;I2C、SPI：传感器、外部芯片或存储器&lt;/li&gt;
&lt;li&gt;I2S、PCM：数字音频数据传输&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;驱动层负责把硬件状态转换成软件可以识别的数据或事件，应用层再决定怎样处理。&lt;/p&gt;
&lt;p&gt;“耳机入盒”背后涉及盒仓状态检测、TWS 对耳同步、按键禁用、ANC 和音乐处理、DAC 控制及主从切换；关盖后还要关闭蓝牙并进入 &lt;code&gt;FUNC_IDLE&lt;/code&gt;或 charge等其他模式。处理不完整，可能导致左右耳状态不一致、盒内仍播放声音或关盖后仍连接手机。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
