之前做過的項(xiàng)目有需要通過音頻口通信用方波來收發(fā)數(shù)據(jù),由于這方面的資料比較少,下面就介紹下其原理,希望能給大家?guī)椭?/p>
一. 音頻通信簡介
大家應(yīng)該都知道支付寶聲波支付和拉卡拉吧,它們都是利用手機(jī)的音頻口(手機(jī)耳機(jī)口)來實(shí)現(xiàn)全雙工的通信(手機(jī)與設(shè)備之間的雙向通信)。其優(yōu)點(diǎn)是低成本,編碼芯片成本低,手機(jī)的 3.5mm 通信接口廣泛。
二. 市場(chǎng)應(yīng)用
支付寶聲波支付
手機(jī)刷卡器
皮膚檢測(cè)儀
檢測(cè)如甲醛、氣壓、溫度、濕度等等
心率、血壓等等
.......................

三. 通信原理
手機(jī)上用的耳機(jī)大多都是3.5mm的四芯座,在這四個(gè)芯中,分別是:地(GND)、左聲道(L)、右聲道(R)和線控開關(guān)(MIC)。
左右聲道作用:向外設(shè)擴(kuò)展頭供電和發(fā)送數(shù)據(jù)
MIC作用:接收數(shù)據(jù)
市場(chǎng)上耳機(jī)主要有兩種標(biāo)準(zhǔn):國內(nèi)標(biāo)準(zhǔn)(如下圖所示),國際標(biāo)準(zhǔn)(MIC和GND和國內(nèi)標(biāo)準(zhǔn)是反的,其他一樣)

聲音波形調(diào)制有調(diào)幅(AM)、調(diào)頻(FM)和調(diào)相(PM)三種,而調(diào)制又有模擬調(diào)制和數(shù)字調(diào)制之分。(模擬調(diào)制和數(shù)字調(diào)制基本知識(shí)參考來源)
模擬調(diào)制:這種編碼方式是將數(shù)字?jǐn)?shù)據(jù)調(diào)制成模擬信號(hào)進(jìn)行傳輸。通常采用三種模擬信號(hào)的載波特性(振幅、頻率和相位)之一來表示被調(diào)制的數(shù)字?jǐn)?shù)據(jù),并由此產(chǎn)生三種基本調(diào)制方式。

(1) 幅移鍵控(ASK)法
ASK(Amplitude Shift Keying)是使用載波頻率的兩個(gè)不同振幅來表示兩個(gè)二進(jìn)制值。在一般情況下,用振幅恒定載波的存在與否來表示兩個(gè)二進(jìn)制字。ASK方式的編碼效率較低,容易受增益變化的影響,抗干擾性較差。在音頻電話線路上,一般只能達(dá)到 1200 b/s的傳輸速率。
(2) 頻移鍵控(FSK)法
FSK(Frequency Shift Keying)是使用載波頻率附近的兩個(gè)不同頻率來表示兩個(gè)二進(jìn)制值。FSK比ASK的編碼效率高,不易受干擾的影響,抗干擾性較強(qiáng)。在音頻電話線路上的傳輸速率可以大于1 200 b/s。
(3) 相移鍵控(PSK)法
PSK(Phase Shift Keying)是使用載波信號(hào)的相位移動(dòng)來表示二進(jìn)制數(shù)據(jù)。在PSK方式中,信號(hào)相位與前面信號(hào)序列同相位的信號(hào)表示 0,信號(hào)相位與前面信號(hào)序列反相位的信號(hào)表示 1。PSK方式也可以用于多相的調(diào)制,例如在四相調(diào)制中可把每個(gè)信號(hào)序列編碼為兩位。PSK方式具有很強(qiáng)的抗干擾能力,其編碼效率比FSK還要高。在音頻線路上,傳輸速率可達(dá) 9600b/s。
這些信號(hào)調(diào)制技術(shù)主要用于調(diào)制解調(diào)器(Modem)中。在實(shí)際的Modem中,一般將這些基本的調(diào)制技術(shù)組合起來使用,以增強(qiáng)抗干擾能力和編碼效率。常見的組合是PSK和FSK方式的組合或者PSK和ASK方式的組合。
數(shù)字信號(hào)編碼: 對(duì)于傳輸數(shù)字信號(hào)來說,最簡單的信號(hào)編碼方法是用信號(hào)的兩個(gè)不同電壓值來表示兩個(gè)二進(jìn)制數(shù)據(jù)。例如,用無電壓來表示 0,用恒定的正電壓表示 1;也可用正電壓表示 1,而用負(fù)電壓表示 0。然而,為了提高信號(hào)抗干擾能力,并且便于信號(hào)接收同步,通常采用更為有效的信號(hào)編碼方法。
常用的數(shù)字信號(hào)編碼有不歸零 NRZ (Non Return to Zero)碼、差分不歸零DNRZ 碼、曼徹斯特(Manchester)碼及差分曼徹斯特(Differential Manchester)碼等。
(1) NRZ碼
NRZ碼是用信號(hào)的幅度來表示二進(jìn)制數(shù)據(jù)的,通常用正電壓表示數(shù)據(jù)“1”,用負(fù)電壓表示數(shù)據(jù)“0”,并且在表示一個(gè)碼元時(shí),電壓均無需回到零,故稱不歸零碼。NRZ碼的特點(diǎn)是一種全寬碼,即一位碼元占一個(gè)單位脈沖的寬度。全寬碼的優(yōu)點(diǎn):一是每個(gè)脈沖寬度越大,發(fā)送信號(hào)的能量就越大這對(duì)于提高接收端的信噪比有利;二是脈沖時(shí)間寬度與傳輸帶寬成反比關(guān)系,即全寬碼在信道上占用較窄的頻帶,并且在頻譜中包含了碼位的速度。

NRZ碼的主要缺點(diǎn)是:當(dāng)數(shù)據(jù)流中連續(xù)出現(xiàn)0 或1時(shí),接收端很難以分辨1個(gè)信號(hào)位的開始或結(jié)束,必須采用某種方法在發(fā)送端和接收端之間提供必要的信號(hào)定時(shí)同步。同時(shí),這種編碼還會(huì)產(chǎn)生直流分量的積累問題,這將導(dǎo)致信號(hào)的失真與畸變,使傳輸?shù)目煽啃越档?,并且由于直流分量的存在,使得無法使用一些交流耦合的線路和設(shè)備。因此,一般的數(shù)據(jù)傳輸系統(tǒng)都不采用這種編碼方式。
(2) DNRZ碼
DNRZ碼是一種NRZ碼的改進(jìn)形式,它是用信號(hào)的相位變化來表示二進(jìn)制數(shù)據(jù)的,一個(gè)信號(hào)位的起始處有跳變表示數(shù)據(jù)“1”,而無跳變表示數(shù)據(jù)“0”。DNRZ碼不僅保持了全寬碼的優(yōu)點(diǎn),同時(shí)提高了信號(hào)的抗干擾性和易同步性。
近年來,越來越多的高速網(wǎng)絡(luò)系統(tǒng)采用了DNRZ碼,成為主流的信號(hào)編碼技術(shù),在FDDI、100BASE-T及100VG-AnyLAN等高速網(wǎng)絡(luò)中都采用了DNRZ編碼。其原因是在高速網(wǎng)絡(luò)中要求盡量降低信號(hào)的傳輸帶寬,以利于提高傳輸?shù)目煽啃院徒档蛯?duì)傳輸介質(zhì)帶寬的要求。而DNRZ編碼中的碼元速率與編碼時(shí)鐘速率相一致,具有很高的編碼效率,符合高速網(wǎng)絡(luò)對(duì)信號(hào)編碼的要求。同時(shí),為了解決數(shù)據(jù)流中連續(xù)出現(xiàn)0 或1時(shí)所帶來的信號(hào)編碼問題,通常采用兩級(jí)編碼方案,第一級(jí)是預(yù)編碼器,對(duì)數(shù)據(jù)流進(jìn)行預(yù)編碼,使編碼后的數(shù)據(jù)流不會(huì)出現(xiàn)連續(xù) 0 或連續(xù) 1,常用的預(yù)編碼方法有4B5B、5B6B等;第二級(jí)是DNRZ編碼,實(shí)現(xiàn)物理信號(hào)的傳輸。這種兩級(jí)編碼方案的編碼效率可達(dá)到 80%以上。例如,在4B5B編碼中,每4位數(shù)據(jù)用5位編碼來表示,即4位數(shù)據(jù)就會(huì)增加 1 位的編碼開銷,編碼效率仍為80%。
(3) 曼徹斯特碼
在曼徹斯特碼中,用一個(gè)信號(hào)碼元中間電壓跳變的相位不同來區(qū)分?jǐn)?shù)據(jù)“1”和“0”,它用正的電壓跳變表示“0”;用負(fù)的電壓跳變表示“1”。因此,這種編碼也是一種相位碼。由于電壓跳變都發(fā)生在每一個(gè)碼元的中間,接收端可以方便地利用它作為位同步時(shí)鐘,因此這種編碼也稱為自同步碼。10Mb/s 以太網(wǎng)(Ethernet)采用這種曼徹斯特碼。

(4) 差分曼徹斯特碼
差分曼徹斯特碼是一種曼徹斯特碼的改進(jìn)形式,其差別在于:每個(gè)碼元的中間跳變只作為同步時(shí)鐘信號(hào);而數(shù)據(jù)“0”和“1”的取值是用信號(hào)位的起始處有無跳變來表示,若有跳變則為“0”;若無跳變則為“1”。這種編碼的特點(diǎn)是每一位均用不同電平的兩個(gè)半位來表示,因而始終能保持直流的平衡。這種編碼也是一種自同步編碼。
令牌環(huán)(Token-Ring)網(wǎng)采用這種差分曼徹斯特編碼。
這兩種曼徹斯特編碼主要用于中速網(wǎng)絡(luò)(Ethernet為 10 Mb/s;Token-Ring最高為16 Mb/s)中,而高速網(wǎng)絡(luò)并不采用曼徹斯特編碼技術(shù)。其原因是它的信號(hào)速率為數(shù)據(jù)速率的兩倍,即對(duì)于 10 Mb/s的數(shù)據(jù)速率,則編碼后的信號(hào)速率為 20 Mb/s,編碼的有效率為 50%。對(duì)于 100 Mb/s的高速網(wǎng)絡(luò)來說,200 Mb/s的信號(hào)速率無論對(duì)傳輸介質(zhì)的帶寬的要求,還是對(duì)傳輸可靠性的控制都未免太高了,將會(huì)增加信號(hào)傳輸技術(shù)的復(fù)雜性和實(shí)現(xiàn)成本,難以推廣應(yīng)用。因此,高速網(wǎng)絡(luò)主要采用兩級(jí)的DNRZ編碼方案,而中速網(wǎng)絡(luò)采用曼徹斯特編碼方案,盡管它增加了傳輸所需的帶寬,但在實(shí)現(xiàn)起來簡單易行。

要實(shí)現(xiàn)手機(jī)端和擴(kuò)展頭的全雙工通信,必須滿足2個(gè)條件:1.信號(hào)必須在音頻頻率之內(nèi);2.需要是低功耗的。第一個(gè)條件限制了信號(hào)帶寬,第二個(gè)條件限制了成本和功率。在這2種限制條件下,主要有2種方式實(shí)現(xiàn)這種同時(shí)的雙向通信:FSK調(diào)制和基于曼徹斯特編碼的直接數(shù)字通信。
這里主要講解下通過數(shù)字調(diào)制中的FSK調(diào)頻調(diào)制方波。FSK有2FSK(2進(jìn)制調(diào)制)、4FSK(4進(jìn)制調(diào)制)、8FSK(8進(jìn)制調(diào)制)等等。
由于在數(shù)字系統(tǒng)中,使用的是0、1表示的二進(jìn)制數(shù)據(jù),在這里,我使用了2FSK來作為信號(hào)的調(diào)制。
2FSK調(diào)頻調(diào)制方波的原理:用一個(gè)頻率表示1,另一個(gè)不同的頻率表示0。比如使用613Hz的信號(hào)代表0,1226Hz的信號(hào)代表1。如下圖所示

方波可以用Cool Edit Pro工具生成,方便調(diào)試和研究。具體方法是:
打開工具,新建文件,然后會(huì)彈出下面窗口

這里根據(jù)你需求設(shè)置波形的采樣率,聲道和采樣精度(一般是44100HZ,單聲道,16位采樣精度)
接下來選擇菜單項(xiàng)-生成

你會(huì)看到如下界面,這里可以通過設(shè)置不同頻率和設(shè)置調(diào)味為方波生成波形了


那么在iOS下如何接收方波解析和發(fā)送方波咧?
每段波形其實(shí)是由N(N=采樣率/位持續(xù)時(shí)間(每位數(shù)據(jù)頻率))個(gè)有符號(hào)的數(shù)據(jù)點(diǎn)組成,這些有正有負(fù)的數(shù)組成了一段段波形,比如波形0則有N個(gè)127組成的波峰波形或者N個(gè)-128組成的波底波形,而波形1則有N/2個(gè)波峰+N/2個(gè)波低組成,這個(gè)波峰波底具體數(shù)值定義可自已靈活定義,如下圖所示

舉個(gè)例子:比如發(fā)送個(gè)數(shù)據(jù)5,其二進(jìn)制數(shù)據(jù)為0000 0101 ,這里設(shè)置采樣率為44100HZ,每段波形時(shí)間頻率1000HZ,定義波形為0 波峰上全是44個(gè)127,波形為0 波底上全是44個(gè)-128,波形為1的數(shù)據(jù) 半個(gè) 波峰上全是44/2個(gè)127,半個(gè) 波底上全是44/2個(gè)-128,那么5 生成波形的數(shù)據(jù)就是 0(44個(gè)127) 0 (44個(gè)-128) 0(44個(gè)127) 0 (44個(gè)-128) 0(44個(gè)127)1(44/2個(gè)-128+44/2個(gè)127)0(44個(gè)-128)1(44個(gè)127)。知道這個(gè)原理了方波解析和發(fā)送數(shù)據(jù)就簡單了。
音頻使用的基本步驟如下:
1.設(shè)置音頻格式和采樣率
mAudioFormat.mSampleRate=AUDIO_SAMPLE_RATE;//采樣率
mAudioFormat.mFormatID=kAudioFormatLinearPCM;//PCM格式
mAudioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
mAudioFormat.mFramesPerPacket = 1;//每個(gè)數(shù)據(jù)包多少幀
mAudioFormat.mChannelsPerFrame = kChannels/2;//1單聲道,2立體聲
mAudioFormat.mBitsPerChannel = 16;//語音每采樣點(diǎn)占用位數(shù)
mAudioFormat.mBytesPerFrame = mAudioFormat.mBitsPerChannel*mAudioFormat.mChannelsPerFrame/8;//每幀的bytes數(shù)
mAudioFormat.mBytesPerPacket = mAudioFormat.mBytesPerFrame*mAudioFormat.mFramesPerPacket;//每個(gè)數(shù)據(jù)包的bytes總數(shù),每幀的bytes數(shù)*每個(gè)數(shù)據(jù)包的幀數(shù)
mAudioFormat.mReserved = 0;
2.設(shè)置remote io unit的渲染回調(diào),從輸入硬件獲得采樣傳入到回調(diào)函數(shù)進(jìn)行渲染,從而獲得錄音數(shù)據(jù)解析數(shù)據(jù). 向回調(diào)函數(shù)填數(shù)據(jù),從而向輸出硬件提供數(shù)據(jù)進(jìn)行放音發(fā)送數(shù)據(jù).
CheckError(AudioUnitSetProperty(toneUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, disable, &mAudioFormat, sizeof(mAudioFormat)), "couldn't set the remote I/O unit's output client format");
CheckError(AudioUnitSetProperty(toneUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, enable, &mAudioFormat, sizeof(mAudioFormat)), "couldn't set the remote I/O unit's input client format");
回調(diào)函數(shù)OSStatus RenderTone(
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData);通過這個(gè)回調(diào)函數(shù)就可以發(fā)送數(shù)據(jù)和解析數(shù)據(jù)
數(shù)據(jù)編碼發(fā)送具體代碼可參考demo里,知道了編碼原理,解碼對(duì)你來說就簡單了
參考文檔:
http://blog.csdn.net/it1988888/article/details/9073767
http://www.seeedstudio.com/wiki/index.php?title=Hijack
http://blog.csdn.net/xiejx618/article/details/9790709