介绍:
BT ,bluetooth ,硬件的厂家有 realtek , Broadcom, csr ,rad 等,我了解到的,前两者在 arm android 上集成的比较多,如 rockchip 平台上rtl8723bs ,ap6212,ap6210, ap6335.等。后者 csr rda 没怎么接触过,听说终端设备上用的比较多。
硬件:
Arm adroid 机子上的蓝牙的硬件几乎都是以模块的形式出现,一般同时封装了 WiFi 和 蓝牙,有的甚至还封装了FM. 管脚为 44pin
蓝牙和主控的连接是串口,需要用到串口的流控:cts rts ,(有的例外,如 realtek rtl8723 可把 芯片cts 接地)。
并且上电,复位和 wake 的几个 gpio 要配置正确,同时,32k 的慢时钟是需要的,不然有可能造成蓝牙打不开的情况,32k 的时钟,一般在 rtc (8563),或者pmu ,或者其他地方取。
在蓝牙用到实时通话(hfp , hfp client)的过程中,还需要pcm/i2s 的连接,注意 in 和 out 多分析一下,容易反,其标识容易混淆。
软件:
蓝牙软件实现比较复杂,对比了一下 android5.1 和 android6.0 发现很大的区别,由于项目需要,把 android5.1 的蓝牙部分移植到了 6.0上,花了相当大的经历。
Android6.0 上蓝牙相关代码位置介绍一下:
1 package/app/blutooth
这里不仅仅是蓝牙的app 层的东西,还有和蓝牙协议栈通信的 Jni , 和 api 通信的service
2 frame/base/core/java/android/bluetooth/
这里是 api 的一些东西,实现和 蓝牙 service 通信的 aidl 接口也是在这实现的。
3 system/bt
蓝牙协议栈,android5.1 放在 externel/bluedroid 里面的
4 device/common/bluetooth/libbt
libbt-vendor 不同厂家私有的一些蓝牙定义。
5 ./hardware/libhardware/include/hardware/bluetooth.h
蓝牙的一些头文件。
6 内核的 dts 以及蓝牙rfkill 的电源的支持。
移植过程:
蓝牙在android 上连耳机,连手机电脑的功能,实现比较简单,差不多底层移植好后就能实现了,由android 本身移植好了。
主要是移植音响的功能,也就是做为设备端,让手机连。使设备播放音乐。
到这里的时候就会去查看蓝牙的一些场景:专业术语叫 profile
如手机,平板功能,都是用的 a2dp, hfp, avrcp 等 profile ,而做为音响,耳机的时候是 a2dp sink , avrcp , hfp client 的profile .,另外还一些我没用过的 profile 如: SPP ,HID
这里需要说明的一点,目前蓝牙对一些 profile 的硬件通路,很多没做过蓝牙的人几乎会犯错:
A2dp / A2dp sink :虽然是播放音乐用的,但是他并没有走 pcm 接口,而是走的串口。这时很多人会计算串口波特率,觉得播放音乐完全不够用,以为串口是 115200 为最高波特率,但是实际在蓝牙串口上,波特率设置在3m左右,一般在蓝牙芯片公司给出的 config 文件里可以看到。
Hfp / Hfp client : 实时语音通话,这个时候是走的pcm (软件中有 SCO 的字样)
A2dp sink 的移植看起来比较简单,不需要考虑硬件通路,反正走串口,只需要讲蓝牙过来的音频数据播放到喇叭就可以。
- 在哪取蓝牙过来的音频数据,怎么能让蓝牙进入音频数据过来的模式?
答案是这部分android 已经做好了。我们只需要配置一下 packages/apps/Bluetooth/res/values/config.xml 里的profile_supported_a2dp_sink 为 true 就可以了
他会模拟出一个 app 可以用声音源: AudioSource.BLUETOOTH_A2DP ,我们只需要用new AudioRecord(AudioSource.BLUETOOTH_A2DP,xxxx), 我这边没有用 AudioSource.BLUETOOTH_A2DP 这个名字,直接用的数字11代替,因为要用到这个 AudioSource.BLUETOOTH_A2DP ,其他地方要改很多东西。
2 蓝牙音频数据的播放如何实现?
这个得自己写,我这边主控公司给出的技术支持,写了2个线程的方法,一个线程负责录音,一个线程负责播放。
这个时候,很多人很问一个问题,蓝牙是串口过来的东西,怎么可以直接用AudioRecord 来读数据呢?对这个问题,如果不深入理解,都是迷茫的。后查资料并读 system/bt 协议栈会发现,协议栈通过 audio_a2dp_hw.c btif_media_task.c等实现声卡的模拟,对上层来说就是多了一个蓝牙声卡,可以对它读写控制。蓝牙声卡的模拟实现的数据传输方式是:socket 。Socket 文件地址 /data/misc/bluedroid/.a2dp_ctrl 和 /data/misc/bluedroid/.a2dp_data
实现录音播放后,a2dp sink 是可以听到手机传过来的声音了。
HFP client 移植主要考虑入口 和线程实现,还有 pcm 接口的配置
1 如何进入 hfp client 的profile ,进入后怎么把数据丢到硬件pcm 上去?
同样是配置
packages/apps/Bluetooth/res/values/config.xml 的 profile_supported_hfpclient 为true ,android 已经实现了它的功能。
我们在 java 的找到接口(hfp_enable)后
./packages/apps/Bluetooth/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java:2145: mAudioManager.setParameters("hfp_enable=true");
在hardware 层来实现其功能,我这里还是主控厂家给的补丁。写了4个线程,2个收2个发:流程如下:
downlink 表示 , 远端电话语音信号-> 手机蓝牙-> AP6212 -> 3368 I2S1 PCM_IN 8K- > 3368 I2S0 I2S_SDO 48K-> ES8316 DAC
uplink表示 , CX20921 ADC->3368 I2S0 I2S_SDI 48K -> 3368 I2S1 PCM_OUT 8K -> AP6212 -> 手机蓝牙->远端电话语音信号
2 硬件上怎么设计能出声音?
硬件设计有几种方式
1,拿主控的一路 i2s/pcm 出来,直接接到蓝牙 pcm 。这个直接读写这一路 i2s
2,蓝牙pcm 接到 主控的codec 上。这个切换 codec 的通路。
我们用的第一种方式
3 pcm 参数的配置:可能是个人以前很少用到 pcm 格式,只知道 i2s 的几种格式,如 标准 i2s ,左右对其什么的。
Pcm 也有几种格式,1time delay ,2 time delay 等等。得注意主控 i2s 和 pcm 的配置。当然这个时候示波器是相当重要的,可以查一下配置是否正确,
Pcm 的格式4线的定义是 clk ,sync ,in ,out . clk 一般在256K ,sync 8k ,sync 的波形是差不多一个 slot 的高电平,其余是低电平。
I2s 的格式4线的定义是 ,bclk .lrclk ,in ,out . clk 一般在2.82m ,lrclk 为采样率,44.1k ,8k 都有
4 蓝牙pcm 采样率和播放到喇叭 codec 采样率不一致,怎么调?
采用重采样,直接采用 externel/speex 的 speex 做重采样。
5 通道数不一致,有的通道有杂音,声音不能调大小?
还是在 hardware 层做算法处理,如丢弃声道,mix 声道,大小按规律去更修改 pcm 数据的大小。
AVRCP 的控制
这个简单,直接调用 api ,发送一个play pause 等命令
另外再说一下,花时间最多的问题: android 6.0 上。A2dp sink 老是出 system crash. 查了很久。因为它是偶现的,比如两个小时才出一次,不管是用 addr2line 定位文件位置,还是看 /data/tombstone 文件查看都没有进展,有兴趣的话可以帮查一下问题:
05-07 17:15:46.901 2474 2501 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0xfb0b1ff8 in tid 2501 (bluedroid wake/)
05-07 17:15:47.006 229 229 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
05-07 17:15:47.006 229 229 F DEBUG : Build fingerprint: 'Android/rk3368_64/rk3368:6.0.1/MOB30J/user.wade.20180507.150808:userdebug/test-keys'
05-07 17:15:47.007 229 229 F DEBUG : Revision: '0'
05-07 17:15:47.007 229 229 F DEBUG : ABI: 'arm'
05-07 17:15:47.008 229 229 F DEBUG : pid: 2474, tid: 2501, name: bluedroid wake/ >>> com.android.bluetooth <<<
05-07 17:15:47.008 229 229 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xfb0b1ff8
05-07 17:15:47.046 229 229 F DEBUG : r0 aaeb0ed8 r1 00000000 r2 00000108 r3 aaeb1258
05-07 17:15:47.046 229 229 F DEBUG : r4 aaeb1ba0 r5 00000380 r6 fffffc80 r7 00ef0004
05-07 17:15:47.046 229 229 F DEBUG : r8 f74f1eb8 r9 aaeb0ee8 sl f74f1eb8 fp f4249aac
05-07 17:15:47.046 229 229 F DEBUG : ip 00000000 sp e2892288 lr 00ef0050 pc f74bc394 cpsr a00e0030
05-07 17:15:47.055 229 229 F DEBUG :
05-07 17:15:47.055 229 229 F DEBUG : backtrace:
05-07 17:15:47.055 229 229 F DEBUG : #00 pc 0002f394 /system/lib/libc.so (dlmalloc_real+1303)
05-07 17:15:47.055 229 229 F DEBUG : #01 pc 000fc241 /system/lib/hw/bluetooth.default.so (osi_malloc+8)
05-07 17:15:47.055 229 229 F DEBUG : #02 pc 0009c469 /system/lib/hw/bluetooth.default.so (GKI_getbuf+6)
05-07 17:15:47.056 229 229 F DEBUG : #03 pc 0005bc91 /system/lib/hw/bluetooth.default.so (btif_media_sink_enque_buf+72)
05-07 17:15:47.056 229 229 F DEBUG : #04 pc 0003cf17 /system/lib/hw/bluetooth.default.so
05-07 17:15:47.056 229 229 F DEBUG : #05 pc 0008d8f7 /system/lib/hw/bluetooth.default.so (bta_av_stream_data_cback+162)
05-07 17:15:47.056 229 229 F DEBUG : #06 pc 000da5df /system/lib/hw/bluetooth.default.so (avdt_scb_event+70)
05-07 17:15:47.056 229 229 F DEBUG : #07 pc 000dac75 /system/lib/hw/bluetooth.default.so (avdt_ad_tc_data_ind+64)
05-07 17:15:47.057 229 229 F DEBUG : #08 pc 000eb5c7 /system/lib/hw/bluetooth.default.so (l2c_csm_execute+3486)
05-07 17:15:47.057 229 229 F DEBUG : #09 pc 000e600f /system/lib/hw/bluetooth.default.so (l2c_rcv_acl_data+4018)
05-07 17:15:47.057 229 229 F DEBUG : #10 pc 000fe96b /system/lib/hw/bluetooth.default.so
05-07 17:15:47.057 229 229 F DEBUG : #11 pc 000ff93f /system/lib/hw/bluetooth.default.so
05-07 17:15:47.057 229 229 F DEBUG : #12 pc 000415af /system/lib/libc.so (_ZL15__pthread_startPv+30)
05-07 17:15:47.058 229 229 F DEBUG : #13 pc 0001918b /system/lib/libc.so (__start_thread+6)
05-07 17:15:47.532 229 229 F DEBUG :
05-07 17:15:47.532 229 229 F DEBUG : Tombstone written to: /data/tombstones/tombstone_05
05-07 17:15:47.532 229 229 E DEBUG : AM write failed: Broken pipe
05-07 17:15:47.539 564 595 I BootReceiver: Copying /data/tombstones/tombstone_05 to Dr
最后解决这个问题的办法,我采取了把 5.1 的蓝牙部分全部移植到 6.0上。测试没有问题。难道是 android 6.0 对a2dp sink 的支持还不到位?