基于Realtek“RTL8753BFR 車載藍牙棒”開發(fā)的音訊連接器設(shè)計
一、前言
隨著藍牙技術(shù)的普及,越來越多的舊設(shè)備早期因無藍牙功能而造成無法及時享用時下日益便捷的藍牙共享功能,因此采用目前流行的“無線藍牙棒”的方式接入藍牙是一個不錯的解決方案選擇,我們開發(fā)的藍牙棒就是一個為沒有藍牙功能的計算機或車載設(shè)備提供一個功能的設(shè)備,打開它,連接到汽車或電腦上,再打開移動藍牙設(shè)備搜索移動藍牙設(shè)備->配對,配對成功就可以使用了 ,它其實是一個藍牙轉(zhuǎn)換器的裝置。其詳細的使用方法如下:
首先將它的USB端插入手機充電器的USB插口給其供電,另一3.5插頭插入音響音頻輸入端,如果輸入端是蓮花插座,應(yīng)該找一條3.5轉(zhuǎn)蓮花插頭音頻線連接,然后打開音響,按照下列步驟操作:
1、首先先打開音響,然后再打開手機。
2、在手機的設(shè)置中,打開藍牙功能。
3、這時手機會自動搜尋周圍藍牙設(shè)備。
4、如果沒有搜尋到你的藍牙音響設(shè)備,請繼續(xù)再點擊搜索,如果已經(jīng)搜索到了你的藍牙轉(zhuǎn)換器,手機便自動連接,并顯示配對成功。
5、如果配對成功,音響便“咕咚”的一聲,表示配對成功。
6、這時用手機就可以播放所儲存的音樂,也可以聯(lián)網(wǎng)進行音樂播放,并且可以控制藍牙音響音量大小。
為了實現(xiàn)上述的功能,我們采用在業(yè)內(nèi)深耕多年長期占據(jù)市場領(lǐng)先地位的Realtek 半導(dǎo)體的藍牙科技技術(shù),其強大的音頻計算處理及高保真的音頻效果讓廣大用戶愛不釋手。Realtek 螃蟹也是業(yè)內(nèi)資深玩家的不二選擇。RTL8753B是REALTEK瑞昱首款完整的TWS真無線藍牙耳機一體化方案,支持藍牙5.1,主從雙發(fā),無縫切換。無線對藍牙棒,支持無線通話、HFP1.7、HSP1.2、A2DP1.3、AVRCP1.6、SPP1.2、PBAP1.0等多種工作模式。具有雙耳通話功能。它還內(nèi)置了鋰電池充電管理,內(nèi)置過壓、過流、欠壓保護等電池防護裝置。在擴展性方面,支持三路LED驅(qū)動,支持觸摸IC控制,支持模擬和數(shù)字麥克風(fēng)輸入,并且支持雙麥克風(fēng)。在降噪方面,支持降噪功能和環(huán)境音監(jiān)聽模式,可以說是一款高性能的全能的TWS真無線藍牙耳機一體化方案?,F(xiàn)在用這款新推出的芯片來設(shè)計一款藍牙棒可以說是非常合適的選擇,不僅開發(fā)簡單快捷,而且成本功耗都非常的滿足日益飛速增長的市場需求。資深人士的保守估計,“藍牙棒”市場近年可達千億市場規(guī)模,未來的競爭可謂百花齊放。采用瑞昱RTL8753BFE主控芯片,支持藍牙5.1版本。工作距離大于15米,可隔墻穿透連接,確保永不掉線。功耗5mA,超長待機,支持200天的待機時長。
二、系統(tǒng)設(shè)計
系統(tǒng)設(shè)計包括兩大部分:硬體及軟體設(shè)計,下面分別闡述:
1、硬件設(shè)計
A) 電源設(shè)計
RTL8753BFE RWS芯片支持兩種電源輸入,一種鋰電池(VBAT:2.8-4.5V),一種電源適配器主要給鋰電池充電( 4.5V – 6.5V),其充電電流可達 400mA,其芯片內(nèi)置充電保護功能和外接環(huán)境保護檢測功能,因此非常適合usb充電方式。芯片內(nèi)部有兩路開關(guān)調(diào)節(jié)器,分別供電1.8V的AVCC/AVCCDRV和1.2V電壓的VDDCORE/VD12_SYN/VD12_RF。
B) 復(fù)位電路
為了保證電路的穩(wěn)定可靠,RTL8753BFE RWS芯片可通過外部的復(fù)位開關(guān)觸發(fā)HW_RST_N腳進行復(fù)位,通常為了節(jié)省成本和空間,該方案僅僅通過外部的充電復(fù)位就可以完成系統(tǒng)的正常復(fù)位(低電平有效保持低脈沖 > 5ms即可)。
C) 時鐘電路
RTL8753BFE RWS芯片有兩路時鐘源,一種是40M的主時鐘源為ARM/BT baseband的正常工作時鐘源,不需要外部負載電容,在MP時需要進行校準,為7~9pf。另外一種是RTC時鐘源32.768k,通常工作在sleep模式下。
D) 音頻電路
音頻的輸入與輸出電路設(shè)計,音頻輸入支持三種方式接入模式(Single end mode、Capless mode、Differential mode),其按照拾音器的不同有四種接入方式(AUX-IN、1-MIC、Dual MIC、Digital MIC),音頻輸出支持S/PDIF接口。由于本產(chǎn)品用于設(shè)計藍牙棒,其輸出設(shè)計成音頻接口即可。在這里,我們需要普及一下音頻接口以兼容大部分產(chǎn)品的應(yīng)用。在2009之前不同的設(shè)備要用不同的耳機,非常的不便,在2009年9月1日國內(nèi)統(tǒng)一標準,規(guī)定耳機插頭2.5mm與3.5mm兩種耳機插頭為國內(nèi)標準插頭,現(xiàn)在國內(nèi)使用的基本都是3.5mm的耳機插頭了,一般的耳機都可以通用了,此外,耳機插頭還有三段與四段,i版與n版等區(qū)別,以及之間的轉(zhuǎn)換等問題,在耳機插頭國內(nèi)統(tǒng)一標準后,耳機的使用已經(jīng)極為的簡易,并且還有非常多的轉(zhuǎn)換設(shè)備拓展耳機的使用范圍,比如一轉(zhuǎn)二共享等、連接電腦、智鍵等等,現(xiàn)在常見的耳機接口都是 3.5mm 音頻接口,分為 3-pole 和 4-pole 兩類,而 4-pole 中又分 Standard 和 OMTP 兩種型號。這是美國人的叫法,國內(nèi)一般把 OMTP 稱為國標,而把稱 Standard 為 CTIA 或美標。一般來說,Standard 型號的耳機插頭上的塑料環(huán)是白色的,而 OMTP 型號插頭上的塑料環(huán)是黑色。見下圖:
其中,3-pole 的接口,顧名思義在插頭上只有 3 個觸點,從尖端到根部依次是左聲道、右聲道、電源地,所以這種接頭的耳機不支持麥克風(fēng)。而 4-pole 的接口支持麥克風(fēng),但從上圖可以清晰地看出,Standard 型號和 OMTP 型號的插頭,其麥克風(fēng)觸點與電源地觸點的位置正好相反。這就是為什么當(dāng)我們將 OMTP 插頭耳機插入 Standard 接口時,聲音聽起來不正常,但按下耳機上的通話按鍵時卻又好了,不過,現(xiàn)在有的電子設(shè)備在設(shè)計時,電路中加入了耳機類型檢測芯片,如 ts3a227e,可以自動檢測耳機接口類型。在這樣的設(shè)備上,以上 3 種耳機都可以正常使用。
D) RF電路設(shè)計
RTL8753BFE 支持 IQM 和 TPM,
—RFIO_IQM 支持雙模, 最大功率 +10dBm 、接收靈敏度 -94dBm @2M EDR
—RFIO_TPM 是專用于BLE最大功率 +4dBm
E) 天線設(shè)計
支持pifa 天線和chip天線、頂針天線,具體的天線設(shè)計可以參考Realtek原廠參考設(shè)計及推薦廠家(萬誠、華新科等等)
F) 外設(shè)引腳設(shè)計
—GPIO(可配置高達32 GPIOs)
—Timer 可配置PWM function
— I2C支持master/slave模式
—SPI支持master / slave模式
—UART(高速串口最大速率達4M)
—GDMA可配置達8 channel數(shù)量且支持Single & multi訪問技術(shù)
—ADC(8-channel /12-bit ADC)
—Keyscan(可達12x20的最大矩陣)
— 支持Q-decoder
— 支持IR接收
—支持SD host兼容SD 2.0
—支持大容量USB傳輸
2、軟件設(shè)計
RTL8753BFE RWS芯片軟件設(shè)計,采用一站式的“傻瓜式”設(shè)計技術(shù),讓所有的客戶輕松構(gòu)建自己的RWS無線耳機系統(tǒng),主要的軟件設(shè)計為以下:
A)MCU 配置
MCU配置工具主要是針對系統(tǒng)控制方面,通過配置工具能夠產(chǎn)生.SCF和.APF文件,其中.SCF文件為系統(tǒng)配置文件,.APF文件為音頻應(yīng)用參數(shù)設(shè)置文件,其主要的目的是:為每個客戶設(shè)計生成定制的配置文件和自定義操作任何源代碼的目的,這些操作全部通過APP UI工具來實現(xiàn)的。這些工具是通過特別授權(quán)的賬號可供客戶下載。
B)DSP配置
DSP配置工具主要是針對音頻方面的配置,其中主要包括有以下內(nèi)容:
—聲音處理:1-mic/2-mic NR(降噪)、AEC(聲回波消除)/ AES(聲回波抑制)、MB-AGC(多波段自動增益控制)、高通濾波器(高通濾波器)、發(fā)送端EQ配置、DAC / ADC設(shè)置
—音頻的A2DP /輸出處理:支持音頻處理功能、MB-AGC、音頻擴大、參數(shù)EQ、發(fā)送端EQ配置、音頻傳遞函數(shù)、模式配置、允許開發(fā)人員以任何想要的順序排列聲音效果。
—無線DSP控制:Bluetooth 鏈路配置
—外圍硬件控制:主要定義I2S接口、仿真解碼接口等等
—SDK開發(fā)接口配置:隨客戶要求自定制可配置成語音和音頻接口
三、測試
3.1 RF 測試
采用安立MT8852B 進行RF 性能參數(shù)測試,具體的測試操作參考儀器說明書。
3.2 產(chǎn)線自動化測試項目
包含了所有MT8852B測試項目,另外附加以下測試項目:
所有MT8852B RF測試項目
藍牙軟件版本測試
設(shè)備名稱測試
自動判定PASS/FAIL,提高30%測試速率
3.3 功能測試
A) 功耗測試
B)聲學(xué)測試
3.4 成品簡易藍牙測試程序(備注:程序來自網(wǎng)絡(luò))
由于項目需要,筆者設(shè)計了一個在安卓平臺開發(fā)的一個程序,能夠用藍牙和下層的單片機通訊。
背景知識
1.藍牙是什么?
一種近距離無線通信協(xié)議,小功率藍牙設(shè)備(一般我們見到的都是)的通訊速率約為1Mbps,通訊距離為10m。
2.藍牙分主從嗎?
分的,藍牙組網(wǎng)的方式是:1主(<8)從。藍牙組的網(wǎng)有個可愛的名字——“微微網(wǎng)”(piconet),藍牙設(shè)備在微微網(wǎng)的地址(注意不是mac地址)是3位,因此一個微微網(wǎng)最多有8臺被激活設(shè)備。設(shè)備的主從角色的分配是在組成微微網(wǎng)時臨時確定的,不過藍牙技術(shù)支持“主從轉(zhuǎn)換”。
3.藍牙設(shè)備都有哪些狀態(tài)?
激活,呼吸,保持,休眠:功率依次遞減。
框架
我們先來看看一般的通訊模型是怎樣的
打開-》建立連接-》通訊-》斷開連接-》關(guān)閉
打開設(shè)備是一切工作的前提,建立連接需要保證兩個藍牙設(shè)備之間的可見性而搜索就是找尋周圍的藍牙設(shè)備(此操作比較占帶寬,通訊時務(wù)必關(guān)掉),通訊就是把兩個設(shè)備用某種方式連接起來(一主一從)然后發(fā)送消息與接收消息,最后需要斷開連接,關(guān)閉設(shè)備。
據(jù)此,設(shè)計UI如下(接收消息按鈕僅僅是為了美觀,代碼中并未實現(xiàn)什么功能):
這個程序僅用到了一個活動:
//實現(xiàn)OnClickListener接口是一個技巧,這樣在活動中給控件設(shè)置監(jiān)聽的時候直接傳this就好了,代碼會簡潔許多
比較重要的是三個內(nèi)部類:
//這三個都是線程類,和藍牙相關(guān)的會阻塞的操作都封裝在它們中
代碼
代碼寫的不是很完善,但完全能夠達到測試的功能
建議把代碼復(fù)制下來,再用IDE工具查看,先看框架(outline),再看細節(jié)
//看代碼有迷惑的地方,再去看前面的類圖,在樹林中迷了路,此時需要登高四望
//所有的輸出均會打印到logcat中,用System.out過濾一下
//注意:使用藍牙,需要聲明BLUETOOTH權(quán)限,如果需要掃描設(shè)備或者操作藍牙設(shè)置,則還需要BLUETOOTH_ADMIN權(quán)限,本實驗兩個權(quán)限都需要
· MainActivity.java
· activity_main.xml
測試
1.拿出兩臺設(shè)備,安裝好程序,完成配對//配對的過程需要人為操作,與這個程序沒有關(guān)系
2.兩臺設(shè)備均打開藍牙(從系統(tǒng)界面或是這個程序的“打開藍牙按鈕均可”)
3.一臺設(shè)備通過adb連接到電腦,點擊“啟動主機”按鈕
4.在另一臺設(shè)備點擊“啟動從機”按鈕
5.在這臺設(shè)備點擊“發(fā)送消息”按鈕
6.不出意外的話,logcat的System.out會打印出三條記錄——1,2,3
MainActivity.java
package com.example.testbluetooth;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.widget.Button;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener {
//藍牙
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set pairedDevices = mBluetoothAdapter.getBondedDevices();
//藍牙狀態(tài)
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {//接收藍牙發(fā)現(xiàn)的消息
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
System.out.println("From mBroadcastReceiver:"+device.getName() + "-" + device.getAddress());
}
}
};
//消息處理
private static final int MESSAGE_READ = 0;
private Handler mHandler = new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case MESSAGE_READ:
byte[] buffer = (byte[])msg.obj;//buffer的大小和里面數(shù)據(jù)的多少沒有關(guān)系
for(int i=0; i<buffer.length; i++){
if(buffer[i] != 0){
System.out.println(buffer[i]);
}
}
break;
}
}
};
//線程
ConnectedThread mConnectedThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//控件
findViewById(R.id.open).setOnClickListener(this);
findViewById(R.id.close).setOnClickListener(this);
findViewById(R.id.search).setOnClickListener(this);
findViewById(R.id.server).setOnClickListener(this);
findViewById(R.id.client).setOnClickListener(this);
findViewById(R.id.send).setOnClickListener(this);
findViewById(R.id.receive).setOnClickListener(this);
findViewById(R.id.paired).setOnClickListener(this);
//注冊廣播
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mBroadcastReceiver, filter); // Don't forget to unregister during onDestroy
}
// a fictional method in the application
//that will initiate the thread for transferring data
void manageConnectedSocket(BluetoothSocket socket){//不知何故,但是在此處用Toast會出現(xiàn)Looper問題//Toast.makeText(this, "A socket opened!", Toast.LENGTH_SHORT).show();
//System.out.println("From manageConnectedSocket:"+socket);
mConnectedThread = new ConnectedThread(socket);
mConnectedThread.start();
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
//設(shè)備支持不支持藍牙和有沒有插藍牙芯片沒有關(guān)系,這個是操作系統(tǒng)的事情
//如果系統(tǒng)不支持藍牙,會返回空,經(jīng)測試,即使沒有藍牙芯片,bluetooth的值可為非空
//但是如果沒有插藍牙芯片,系統(tǒng)會阻塞住
switch (v.getId()) {
case R.id.open:
if (!mBluetoothAdapter.isEnabled()) {//如果藍牙沒有打開
mBluetoothAdapter.enable();//這種打開方式可以跳過系統(tǒng)打開藍牙的界面
}
break;
case R.id.close:
if (mBluetoothAdapter.isEnabled()) {//如果藍牙已經(jīng)打開
mBluetoothAdapter.disable();
}
break;
case R.id.search:
if (!mBluetoothAdapter.isDiscovering()) {//如果藍牙不處于搜索狀態(tài)(即找尋附近藍牙)
mBluetoothAdapter.startDiscovery();//Enabling discoverability will automatically enable Bluetooth.
}
break;
case R.id.server:
new AcceptThread().start();
((Button)findViewById(R.id.client)).setVisibility(View.INVISIBLE);
break;
case R.id.client:
for(BluetoothDevice device:pairedDevices){
new ConnectThread(device).start();
((Button)findViewById(R.id.server)).setVisibility(View.INVISIBLE);
}
break;
case R.id.send:
if(mConnectedThread != null){
byte[] bytes = new byte[]{1,2,3};
mConnectedThread.write(bytes);
}
break;
case R.id.receive:
break;
case R.id.paired:
pairedDevices = mBluetoothAdapter.getBondedDevices();
for(BluetoothDevice device:pairedDevices){
System.out.println("From paired:"+device.getName());
}
break;
default:
break;
}
}
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
// Use a temporary object that is later assigned to mmServerSocket,
// because mmServerSocket is final
BluetoothServerSocket tmp = null;
try {
// MY_UUID is the app's UUID string, also used by the client code
tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("blind_nav", UUID.fromString("0c312388-5d09-4f44-b670-5461605f0b1e"));
} catch (IOException e) { }
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// Keep listening until exception occurs or a socket is returned
while (true) {
try {
//System.out.println("From AcceptThread:");
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
// If a connection was accepted
if (socket != null) {
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(socket);
try {
mmServerSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
}
/** Will cancel the listening socket, and cause the thread to finish */
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) { }
}
}
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(UUID.fromString("0c312388-5d09-4f44-b670-5461605f0b1e"));
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();//這個操作需要幾秒鐘,不是立即能見效的
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(mmSocket);
}
/** Will cancel an in-progress connection, clean up all internal resources, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
//Message msg = new Message();
//msg.what = MESSAGE_READ;
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
}
activity_main.xml
? 場景應(yīng)用圖
? 產(chǎn)品實體圖
? 展示版照片
? 方案方塊圖
? 核心技術(shù)優(yōu)勢
瑞昱RTL8753BFE主控芯片不僅支持藍牙5.1、雙耳通話,還支持HFP1.7、HSP1.2、A2DP1.3、AVRCP1.6、SPP1.2、PBAP1.0等多種耳機模式。
1. Dual mode BT5.1
2. 優(yōu)異的RF性能: 10dBm(typ) transmit power and receiver sensitivity to : -94.0 dBm (typ) 2M EDR; -97.0 dBm (typ) BLE; -106.5 dBm (typ) 125K BLE
3. 支援OTA更新firmware
4. 支援開放式SDK平臺,讓用戶開發(fā)特色化商品
5.超低功耗,播放音樂時電流<8mA
? 方案規(guī)格
1. 支持雙咪降噪功能(可選)
2. 支持光感開關(guān)機
3. 支持自動充電開關(guān)機
4. 支持可設(shè)定化的EQ
評論