前段时间在RK3568平台上调一款UART接口的蓝牙模组,前后折腾了小一周,踩了设备树、电源时序、固件加载、内核配置好几轮坑。这个需求其实很常见:RK3568这颗芯片被大量用在工业平板、边缘计算盒子、商显设备上,很多产品只要蓝牙不要WiFi,或者USB口被4G模组、U盘、摄像头占满了,这时候最省事的方案就是从UART挂一颗蓝牙控制器。但“通过UART接口实现蓝牙主机外设驱动”这句话,在实际落地时比想象中要复杂,因为真正要处理的不是“写一个驱动”,而是把内核蓝牙子系统、串口子系统、设备树、厂商固件、用户态服务这些环节全部打通。
这篇文章我按自己的调试路径来写,从硬件选型、设备树配置、内核选项,到hciattach/BlueZ联调和常见问题排查,尽量把每个“为什么”讲清楚。适合正在做RK3568、RK3588这类瑞芯微平台的BSP工程师,以及想把UART蓝牙方案快速跑起来的嵌入式开发。哪怕你是新手,跟着思路走一遍,也能少踩一半的坑。
1. 先搞清楚硬件选型:UART蓝牙到底要配什么
1.1 不是随便一个蓝牙模块都能当Linux蓝牙主机外设
这是第一个容易踩坑的地方。市面上标着“蓝牙模块”的东西,按工作方式分完全不同的两类。
一类是HC-05、HC-06、JDY-31这类透传模块。它们的协议栈在模块内部,主控MCU只需要通过UART发AT指令,模块自己完成配对和数据透传。很多做单片机的朋友对这个很熟。但如果把这种模块接在RK3568的UART上,Linux系统里根本不会出现hci0,BlueZ那套工具也不认识它,因为它的HCI层被模块封装掉了,相当于你只能把它当成一个“无线串口”用,你需要在应用层自己处理数据分包、断线重连、AT命令交互。
另一类是真正的蓝牙控制器(Controller),比如博通的AP6212、AP6256、BCM43438,瑞昱的RTL8723DS、RTL8821CS,还有正基的AP6xxx系列等。这类芯片内部只有基带和射频,完整的协议栈(HCI以上:L2CAP、SMP、ATT/GATT、RFCOMM等)跑在Linux主机侧。它们通过UART暴露HCI接口,Linux端需要hci_uart驱动来和它通信,然后BlueZ负责上层协议。这样系统里看到的是一个完整的、标准的蓝牙适配器,能连耳机、传文件、跑BLE,行为和USB蓝牙棒完全一样。
我们说的“RK3568 UART接口蓝牙主机外设驱动实现”,指的是后一种。简单说:RK3568是蓝牙主机,外设是一颗需要主机侧跑协议栈的蓝牙控制器。
两种模块的选型区别我整理在下面,方便大家判断自己手上的模块该走哪条路。
| 模块类型 | 典型芯片 | 主控侧协议栈 | Linux呈现 | 适用场景 |
|---|---|---|---|---|
| 透传/Slave模块 | HC-05、HC-06、JDY-31 | 模块内部 | 无hci0,UART纯透传 | MCU级无线串口,不适合Linux原生蓝牙 |
| 主机HCI控制器 | AP6212、AP6256、BCM43438、RTL8723DS | Linux BlueZ栈 | 有hci0,标准蓝牙适配器 | RK3568/Rockchip方案首选 |
提示:如果你拿到的是“杰理蓝牙”、蓝牙水控器这类方案,先确认模块是运行在透传模式还是HCI模式。很多模块本身支持两种模式,通过EEPROM/引脚/指令切换,但驱动实现路径完全不同。
1.2 RK3568的UART资源怎么选
RK3568一共有8路UART,但并不是每路都适合接蓝牙。选择UART时我主要看三点:是否支持硬件流控、引脚mux是否和现有板卡冲突、调试串口和日志串口是否占用了目标口。
硬件流控是第一个优先级。UART蓝牙在初始化时要从默认的115200波特率跳到更高速度(常见1500000、3000000甚至4000000),这个波特率协商过程依赖CTS/RTS信号做握手机制。如果串口不带流控或者板子上没接CTSN/RTSN,跳波特率时经常丢字节,然后出现“Firmware loader timed out”这类错误。
拿RK3568来看,uart0、uart1、uart2、uart3这些都有对应的CTSN/RTSN引脚,但不同引脚组(比如uart2的m0和m1)在芯片pin脚上分布完全不同,实际能用哪组取决于你的PCB设计。如果是用正点原子RK3568这种现成开发板,通常会引出一两个带流控的UART,参考底板原理图就能确认。选好了串口,还要检查它是否被占用:很多板卡的调试串口默认用uart2,内核cmdline里的console=ttyS2,1500000就是它。如果你非要用uart2接蓝牙,就得把console改到别的串口,否则hciattach启动时会提示“Device or resource busy”,因为getty还在占用这个串口。
另一个容易忽略的是电源能力。蓝牙控制器在射频发射时的峰值电流能做到100mA以上,如果LDO选得太小,或者VBAT走线太细,会出现能扫描到设备但连连断断的怪问题。我碰到过一次反复“connection timeout”,最后发现是蓝牙引脚旁边的DC-DC纹波太大,射频灵敏度被干扰了。
1.3 电源、复位和32.768k时钟一个都不能缺
很多“设备树明明配好了但蓝牙就是不起来”的问题,根源在硬件电路上,尤其是三样东西:供电、复位、慢时钟。
供电包括VBAT主供电和VDDIO电平匹配。RK3568的IO一般是3.3V或1.8V,蓝牙模组的VDDIO必须和UART引脚电平一致。接错电平的话,表现为串口能收到数据但内容乱码、hciattach识别芯片型号失败等。
复位脚(BT_REG_ON/BT_RST_N)的主机侧控制逻辑也有一堆讲究。以AP6256为例,规格书要求上电后先拉低复位脚保持一段时间,再拉高让芯片进入工作状态,同时还要保证外部32.768kHz从钟稳定。有些开发板上复位脚通过三极管/场管反相,导致设备树里写的GPIO_ACTIVE_HIGH和实际电平正好反了。所以确认复位和唤醒引脚的有效电平,不能光看原理图,最好用示波器抓一下上电瞬间波形。
32.768k慢时钟在AP62xx系列里是必须的。如果模组没有自带晶振而你又没从主控侧送时钟,蓝牙可能出现:能识别hci0、能扫描,但连接建立后立刻断开,或者扫描间隔异常变长。RK3568的BSP里有专门的clk输出引脚(比如clkout1),设备树的“clocks”属性就是用来配这颗从钟的。
2. 设备树修改:让内核知道这块蓝牙挂在哪个串口
2.1 先开串口,再加蓝牙子节点
RK3568设备树里配UART蓝牙,最标准的做法是在选定的UART节点下挂一个蓝牙子节点。下面是一份基于RK3568 SDK常见写法的示例,以uart2的m1引脚组为例(实际引脚编号请对照你自己的原理图):
&uart2 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&uart2m1_xfer &uart2m1_ctsn &uart2m1_rtsn>; bluetooth { compatible = "brcm,bcm43438-bt"; reset-gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>; vreg_enable-gpios = <&gpio2 23 GPIO_ACTIVE_HIGH>; clocks = <&rk3568_clkout1>; clock-names = "ext_clock"; }; };这段配置里最关键的两行是:pinctrl-0必须同时包含数据引脚xfer和流控引脚ctsn/rtsn,一个都不能漏;蓝牙子节点的compatible会决定内核挂载哪个蓝牙驱动,brcm,bcm43438-bt对应内核里的hci_bcm.c驱动,如果你的模块是瑞昱,compatible通常换成了realtek,rtl8723ds-bt,由rtl_bt相关模块处理。
顺着设备树往下讲,瑞芯微老一点的BSP(比如RK3399时代)习惯用全局的bluetooth-platdata节点,通过BT,reset_gpio、BT,wake_gpio这些属性描述蓝牙。到了RK3568,越来越多SDK转向serdev子树方式,也就是在UART节点下直接挂子节点。两种方式都能跑,但如果你拿到的是别人裁剪过的SDK,先确认内核里有没有对应的驱动实现,别照搬网上老代码。
2.2 pinctrl复用冲突:RK3568上最常见的“驱动不起效”
RK3568的IO复用自由度很高,这是优点也是坑。同一组引脚,一个时刻只能被一个外设功能使用。很多时候蓝牙不起,是因为其它节点先把这组引脚占掉了。
比如uart2m1_xfer这几个引脚,在方案商SDK里可能同时被配置成了spi1、can0或者gpio-fan。虽然你在设备树里给uart2设了status = "okay",但pinctrl子系统解析时,如果另一节点先用pinctrl申请了同一个pin,uart2申请不到pin,串口就不会正常注册,dmesg里也没有明确报错,最多看到“failed to get pinctrl”之类的提示。
排查方法是打开pinctrl的debugfs:
cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins再看对应引脚的当前功能是uart还是哪个别的外设。另外我习惯在改完设备树后用dtc -I dtb -O dts把编译产物反解析出来检查一下,确认&uart2里的status、pinctrl真实生效了,没有因为覆盖节点顺序问题被后面节点覆盖。
2.3 GPIO控制脚的“有效电平”别想当然
设备树里的reset-gpios、vreg_enable-gpios、wake-gpios,一定要确认active电平与实际电路相符。
举个例子:某款开发板原理图上写着BT_REG_ON连接到主控GPIO,芯片内部是“高有效”,但板子上加了一颗NPN三极管做电平转换,GPIO输出高时三极管导通把BT_REG_ON拉低,实际变成了低有效。你在设备树里沿用参考设计的GPIO_ACTIVE_HIGH,结果就是复位脚一直处于无效状态,蓝牙芯片没起来。
排查这类问题最直接的方法是写个小脚本来翻转对应GPIO,边翻边量BT_REG_ON引脚电平:
echo 22 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio22/direction echo 0 > /sys/class/gpio/gpio22/value sleep 0.5 echo 1 > /sys/class/gpio/gpio22/value同时用万用表或示波器量芯片端电压。确认完实际电气逻辑,再回设备树里改flag,不要盲目COPY参考设计。复位时序上,大部分蓝牙控制器要求拉低时间不小于10ms,然后拉高,再等待几十毫秒再开始通过UART下发HCI命令。有些驱动在probe时会自己控制时序,如果你发现初始化经常超时,也可以在hciattach之前手动用GPIO脚本先做一次硬复位,把问题定位到“时序不对”还是“驱动交互不对”。
3. 内核配置与驱动加载:让hci_uart真正跑起来
3.1 内核蓝牙子系统的关键配置项
设备树只是告诉内核“这里有一块蓝牙”,要让它真正工作,内核里必须编入完整的蓝牙协议栈和HCI UART驱动。RK3568的SDK默认内核配置里通常会打开大部分,但你如果是把某个基础内核配置裁剪过,就得手动确认下面这些选项:
Networking support -> Bluetooth subsystem support -> Bluetooth core -> Bluetooth RFCOMM -> Bluetooth BNEP -> Bluetooth HIDP -> Bluetooth device drivers -> HCI UART driver -> UART (H4) protocol support -> Broadcom (BCM) protocol support -> Realtek (RTL) protocol support其中H4是UART HCI的经典协议格式,一帧数据包含4字节的头(Indicate、HCI类型、长度),后面跟数据。但注意:H4协议本身不带流量控制机制,所以必须靠硬件CTS/RTS来保证不丢包。3-Wire(H5)协议则带可靠传输机制(SLIP封装、CRC校验、重传),适合没有硬件流控的场景,但效率比H4低、时序复杂,实现上更容易出问题。能用H4流控的就别选H5。
Broadcom和Realtek协议支持关系到厂商固件能不能自动下载。博通芯片启动后,HCI驱动要通过厂商专用HCI命令把固件烧进芯片的RAM里,这个流程被实现在hci_bcm.c里,需要开启BT_HCIUART_BCM;瑞昱类似,需要BT_HCIUART_RTL。如果这些没开,你会发现hciattach能识别芯片但马上报错或者直接hang住。
3.2 固件文件到底放哪里去了
这是UART蓝牙方案里最没有“文档味”的一步:内核HCI驱动只是驱动框架,芯片的固件数据需要以二进制文件形式放到文件系统里,驱动启动时通过request_firmware机制加载到芯片上。
博通芯片的固件一般存放在:
/lib/firmware/brcm/BCM43438A0.hcd瑞昱芯片的固件一般存放在:
/lib/firmware/rtl_bt/rtl8723ds_fw.bin /lib/firmware/rtl_bt/rtl8723ds_config.bin文件名要和驱动代码里匹配。如果dmesg出现类似“brcm-firmware failed with -2”或者“Direct firmware load for ... failed”,十有八九就是固件文件路径不对、文件名大小写不匹配,或者根文件系统是只读挂载导致没读到。我调试时习惯先在文件系统里手动搜一遍:
find /lib/firmware -iname "*bt*" -o -iname "*bcm*" -o -iname "*rtl*"如果SDK里的固件是放在/vendor/firmware/或者/system/etc/firmware/的,你需要设置firmware_class的搜索路径,或者在启动脚本里把固件目录软链接到/lib/firmware下。很多方案商提供的SDK会把固件放在独立分区,单独挂载,这类问题在换根文件系统时特别容易复现。
3.3 hciattach启动流程与实测记录
设备树、内核配置、固件都到位后,就进入联调阶段了。最传统、也最能暴露问题的启动方式是手动执行hciattach。
假设蓝牙挂在/dev/ttyS4,典型命令如下:
stty -F /dev/ttyS4 115200 hciattach /dev/ttyS4 any -s 1500000 flowany表示让驱动根据UART返回的HCI版本信息自动选择协议(BCM/RTL/H4等),-s 1500000表示协商后的目标波特率,flow打开CTS/RTS硬件流控。命令执行成功时,通常会输出芯片型号信息,接着/dev/hci0设备出现。
我实机跑起来的一段log大致是这样的:
$ hciattach /dev/ttyS4 any -s 1500000 flow Found device with type: 3 ... Device setup complete然后查看蓝牙设备:
hciconfig -a能看到hci0: Type: Primary Bus: UART,BD Address有具体值而不是00:00:00:00:00:00,State是DOWN。接着执行:
hciconfig hci0 up如果没有任何报错,再用hcitool dev确认设备存在,这时蓝牙主机侧的基本链路就算通了。
不过现在的RK3568 SDK里,越来越多的方案不再让用户手动执行hciattach,而是通过内核的serdev机制在设备树匹配后自动注册hci0。典型表现是:你插上UART蓝牙模块,系统启动后直接就有了hci0,不需要额外的用户态attach命令。如果走serdev,内核日志里会初始化和蓝牙相关的信息。这两种方式不冲突,调试时建议先手动确认链路,再切换回自动化脚本,这样出问题时能快速定位是驱动自动加载的哪一环没走通。
4. 应用层联调:从能扫描到能连设备
4.1 先看HCI层是不是真的健康
HCI层通了不代表“能用”,你要确认它的状态是健康的。我最先做的是这几步:
hciconfig -a hcitool dev hcitool infohciconfig -a能看到设备名、类别、PSCAN/ISCAN状态;hcitool info会向蓝牙模块发送读取本地版本信息的命令,如果HCI命令超时,说明UART数据传输还是有问题,需要回到流控和波特率检查。正常输出里能看到Manufacturer、HCI version、LMP version这些字段,如果Manufacturer显示0xffff或者HCI version是0,基本能断定模块没有正常响应。
4.2 经典蓝牙和BLE两种方式分开测
RK3568这类产品的蓝牙使用场景基本是两种:连接耳机音箱(A2DP),或者跑BLE做数据采集(心率、温湿度、ibeacon等)。两条路径的测试命令不一样。
经典蓝牙扫描:
hcitool scanBLE扫描:
hcitool lescan扫描结果里能看到设备的MAC地址和名称。如果lescan没有返回任何结果,优先怀疑天线匹配、模块附近的金属结构件遮挡、以及蓝牙地址是否有效。地址全是00:00:00:00:00:00的设备,很多手机和协议栈会直接忽略,这种情况需要给模块烧写有效BD Address。
配对和连接建议用bluetoothctl这条链路做完整验证:
bluetoothctl power on agent on scan on pair 目标MAC trust 目标MAC connect 目标MAC在BLE场景下,我习惯再用gatttool或者hcitool lecc验证连接参数:
hcitool lecc 目标MAC连接成功后再做GATT读写交互,确认ATT层正常。如果经典蓝牙能扫到但BLE经常超时,往往是天线面积不够或者模块晶振频偏偏大,不是纯软件问题。
4.3 量产时这些细节最容易坑人
如果把UART蓝牙方案做到产品里而不是仅仅开发板调通,有三件事必须提前处理。
第一是BD Address。量产板子的蓝牙芯片如果地址相同或者无效,手机端可能出现“只扫描不连接”的怪现象。很多方案商支持在产线用AT指令或HCI命令写入唯一MAC,建议在烧录工具里就规划好。
第二是蓝牙CLASS和名称。蓝牙CLASS决定了设备在手机蓝牙列表里显示为“耳机”“手机”“电脑”还是“未知设备”。CLASS设置不对,会导致手机端配对界面显示异常。用hciconfig hci0 class 0x000000可以临时设置,正式产品要在启动脚本里固化。
第三是电源管理和睡眠。RK3568做低功耗产品时,蓝牙和WiFi共用电源域很常见。蓝牙连接后不能进入深度睡眠,否则UART时钟停了,主机侧的HCI就会被认为掉线。要正确处理主机和蓝牙之间的wake引脚,才能做到既不耗电又不掉线。
5. RK3568平台调试问题排查与避坑实录
5.1 初始化类故障速查表
下面这些故障是我在RK3568以及周边平台调UART蓝牙时实际遇到过的,整理成速查表方便定位。
| 现象 | 可能原因 | 排查顺序 |
|---|---|---|
| hciattach时报Device or resource busy | 串口被getty/console占用 | 先查systemd getty和cmdline console |
| hciattach识别不了芯片,一直超时 | 复位/电源脚没生效,模块未上电 | 量BT_REG_ON、VDDIO电平,确认设备树GPIO |
| dmesg报firmware load failed | 固件文件缺失、路径不对 | find /lib/firmware确认文件是否存在 |
| 扫描不到任何设备 | 天线、地址无效、射频干扰 | 先看hcitool info是否正常,再查硬件 |
| 能扫描但连接就断 | 慢时钟、电源纹波、流控线没接 | 补32.768k时钟,检查CTS/RTS |
| 波特率协商失败 | 无硬件流控或对端流控状态不对 | 确认ctsn/rtsn引脚配置并接好对应线 |
| UART蓝牙和WiFi互相干扰 | combo芯片共存问题、供电不足 | 检查共享LDO电流,必要时协同调试 |
5.2 扫描不到设备的经典原因
扫描不到设备是最难定位的一类问题,因为软件链路可能全部正常,硬件也看起来都通电了。我按优先级排查:
第一,确认HCI device的状态。如果hcitool dev输出为空,说明HCI都没注册成功,问题在驱动初始化;如果hci0存在但scan没有结果,问题多半在链路后半段。
第二,量一下天线匹配。蓝牙2.4G天线如果被金属外壳完全包住,或者天线的pi型匹配电路焊错,扫描结果会非常差,表现为扫到设备极其困难或者只能近距离扫到一两个。
第三,看蓝牙地址。有些模块出厂地址是无效的(全0或全F),会让对端设备忽略广播。用hcitool cmd 0x03 0x0001 00 11 22 33 44 55临时写入地址再扫描,能快速判断问题。
第四,检查是否被其它信号干扰。RK3568板卡上如果有USB 3.0、大功率DC-DC、甚至WiFi 2.4G同频工作,都可能让蓝牙灵敏度大幅下降。可以用远离干扰源的方式做对比实验,比如只给蓝牙板单独供电,不启动系统其它外设,看扫描距离是否明显变好。
5.3 连接不稳定与数据传输异常的排查
UART蓝牙最常见的稳定性问题,是连接建立后过几秒就断开,或者A2DP音频卡顿。这类问题不要一上来就怀疑蓝牙协议栈,先回到UART物理链路上找原因。
CTS/RTS流控线没接是头号原因。H4协议没有重传机制,UART RX FIFO一旦溢出,HCI数据包就丢了,上层立刻表现为连接断开或数据断流。用示波器看CTS/RTS线上的波形,正常情况下RTS在设备忙时会拉低,如果这个信号一直不变化,检查设备树pinctrl配置里是否漏了ctsn/rtsn。
第二常见的是电源跌落。蓝牙在射频发射时需要较大瞬时电流,如果供电网络阻抗过大,射频发射瞬间电压跌落会导致模块复位或状态机错乱。把示波器探头点在蓝牙模块的VBAT引脚,用单次触发抓发射瞬间的波纹,能看到明显的电压塌陷。解决办法是加大去耦电容、加点串联磁珠、换更大电流的LDO。
第三是主控侧的电源管理。RK3568的内核如果使能了UART的Runtime PM,在蓝牙长时间空闲时串口时钟可能被关闭,导致HCI链路静默。设备树uart节点里加上power-domains的正确配置,或者在启动脚本里对串口做以下操作是常见做法:
echo on > /sys/devices/platform/soc/*/serial*/power/control另外HCI层也有流量控制机制:通过hcitool或btmgmt设置HCI流量控制参数可以缓解,但根治还是要看物理链路。
5.4 RK3568平台独有的几个坑
RK3568相比其它平台有几个特别容易踩的坑,值得单独提一下。
第一个坑是调试串口默认和蓝牙争用。RK3568很多开发板默认console用的是uart2,而且bootloader、内核cmdline、getty三层都会打开它。如果你恰好把蓝牙挂在uart2,看起来就像“hciattach一直在超时”,实际是串口被console占着,数据根本到不了蓝牙。解决办法是在uboot环境变量和内核cmdline里把console改到其它串口,同时移掉systemd对ttyS2的serial-getty服务。
第二个坑是IO域供电。RK3568的IO供电分VCCIO1~VCCIO6等多个域,信号电平取决于对应电源域的电压。如果你把蓝牙接在VCCIO1域,但VCCIO1实际是1.8V,蓝牙模块却要求3.3V电平,串口数据就会乱码。这个问题在复用SDK默认配置时经常出现,尤其是从别的开发板改过来的情况。
第三个坑是和其它功能模块“抢引脚”。RK3568调试OV5695摄像头、BT1120输出、EtherCAT这类功能时,会占用大量GPIO和pinctrl复用。有些引脚表面上看和UART无关,但被设置成某个mux功能后,相邻bank的偏置配置会受影响。我遇到过蓝牙一直扫描不稳定,最后发现是摄像头sensor的上电脚和蓝牙复位脚在同一个bank,摄像头驱动上电时把整个bank的电压域拉了一下,导致蓝牙被瞬时复位。确认方法是在/sys/kernel/debug/gpio里观察同一时刻GPIO状态变化,这类交互问题排查起来很花时间。
5.5 向WiFi/BT Combo方案迁移时的额外提醒
如果你用的不是独立蓝牙芯片,而是AP6212、AP6256、RTL8723DS这种WiFi/BT二合一模组,驱动实现会更复杂,因为涉及WiFi和蓝牙的共存问题。
共存机制在硬件上通过一个PTA接口实现(WiFi和蓝牙之间协商射频调度),软件上则要求两个子系统的驱动同时正确加载。我见过有人先单独调蓝牙,没加载WiFi驱动,此时蓝牙能扫描但A2DP音频会有间歇性“沙沙”声,因为射频调度没有协商,蓝牙传输时WiFi没让路。反过来,如果WiFi在长时间持续传数据,蓝牙连接稳定性也会受影响。
RK3568的SDK里一般会提供集成好的wifi_bt驱动包,或者类似rkwifibt的服务脚本,会统一管理固件下载、设备节点创建、协调层初始化。用这类方案时,优先保留SDK原本的加载方式,别自己手写hciattach脚本。因为兼容性问题不是简单把蓝牙跑起来就行的,需要厂商固件和驱动版本配套。这也是为什么同样是RK3568,正点原子等开发板出厂SDK里蓝牙基本不需要额外配置,开箱就能用。
最后再分享一点个人习惯
把整个UART蓝牙方案的驱动适配流程完整跑过一遍后,我最大的体会是:UART蓝牙的“驱动实现”难点从来不在代码,而在确认“每一层的边界在哪里”。内核hci_uart是现成的,BlueZ是现成的,厂商固件也是现成的,你真正要做的是把设备树pinctrl搞对、把电源时序搞对、把固件路径搞对、把用户态启动脚本搞对。这些环节都不难,但任何一个环节错了,现象都长得一模一样:hci0不出现、扫描没结果、连接就断开。
所以我现在的习惯是:拿到一块UART蓝牙模块,先不写任何设备树,先用USB转串口工具(比如FT231X、CP2104这类方案)在PC上通过串口工具直接发HCI命令,确认模块本身能响应;再到板子上用stty和hciattach手动拉链路,确认UART物理通路正常;最后才写设备树、做自动加载、封装成产品方案。按这个顺序排查,大部分“玄学问题”最后都变成了具体问题——要么引脚配错了,要么固件没放对,要么电源不稳。
这篇文章写到的设备和命令覆盖了RK3568最主流的UART蓝牙方案,但不同SDK、不同模组之间总有差异。如果你正在调的项目用的是别的UART口或者别的芯片型号,建议先按文章里的排查顺序走:从硬件量测开始,再到设备树、内核配置、固件路径、用户态命令,把每一步用输出和日志固定下来,问题一定会暴露出来。UART蓝牙这东西,稳定跑起来之后其实很省心,难的是开头那一公里。