iOS开发之音频口通信-通过方波来收发数据

0.547字数 3200阅读 6494

之前做过的项目有需要通过音频口通信用方波来收发数据,由于这方面的资料比较少,下面就介绍下其原理,希望能给大家帮助。

一. 音频通信简介
大家应该都知道支付宝声波支付和拉卡拉吧,它们都是利用手机的音频口(手机耳机口)来实现全双工的通信(手机与设备之间的双向通信)。其优点是低成本,编码芯片成本低,手机的 3.5mm 通信接口广泛。

二. 市场应用
支付宝声波支付
手机刷卡器
皮肤检测仪
检测如甲醛、气压、温度、湿度等等
心率、血压等等
.......................


三. 通信原理
手机上用的耳机大多都是3.5mm的四芯座,在这四个芯中,分别是:地(GND)、左声道(L)、右声道(R)和线控开关(MIC)。
左右声道作用:向外设扩展头供电和发送数据
MIC作用:接收数据
市场上耳机主要有两种标准:国内标准(如下图所示),国际标准(MIC和GND和国内标准是反的,其他一样)


国内标准

声音波形调制有调幅(AM)、调频(FM)和调相(PM)三种,而调制又有模拟调制和数字调制之分。(模拟调制和数字调制基本知识参考来源)

模拟调制:这种编码方式是将数字数据调制成模拟信号进行传输。通常采用三种模拟信号的载波特性(振幅、频率和相位)之一来表示被调制的数字数据,并由此产生三种基本调制方式。

模拟信号三种调制方式

(1) 幅移键控(ASK)法

ASK(Amplitude Shift Keying)是使用载波频率的两个不同振幅来表示两个二进制值。在一般情况下,用振幅恒定载波的存在与否来表示两个二进制字。ASK方式的编码效率较低,容易受增益变化的影响,抗干扰性较差。在音频电话线路上,一般只能达到 1200 b/s的传输速率。

(2) 频移键控(FSK)法

FSK(Frequency Shift Keying)是使用载波频率附近的两个不同频率来表示两个二进制值。FSK比ASK的编码效率高,不易受干扰的影响,抗干扰性较强。在音频电话线路上的传输速率可以大于1 200 b/s。

(3) 相移键控(PSK)法

PSK(Phase Shift Keying)是使用载波信号的相位移动来表示二进制数据。在PSK方式中,信号相位与前面信号序列同相位的信号表示 0,信号相位与前面信号序列反相位的信号表示 1。PSK方式也可以用于多相的调制,例如在四相调制中可把每个信号序列编码为两位。PSK方式具有很强的抗干扰能力,其编码效率比FSK还要高。在音频线路上,传输速率可达 9600b/s。
这些信号调制技术主要用于调制解调器(Modem)中。在实际的Modem中,一般将这些基本的调制技术组合起来使用,以增强抗干扰能力和编码效率。常见的组合是PSK和FSK方式的组合或者PSK和ASK方式的组合。

数字信号编码: 对于传输数字信号来说,最简单的信号编码方法是用信号的两个不同电压值来表示两个二进制数据。例如,用无电压来表示 0,用恒定的正电压表示 1;也可用正电压表示 1,而用负电压表示 0。然而,为了提高信号抗干扰能力,并且便于信号接收同步,通常采用更为有效的信号编码方法。

常用的数字信号编码有不归零 NRZ (Non Return to Zero)码、差分不归零DNRZ 码、曼彻斯特(Manchester)码及差分曼彻斯特(Differential Manchester)码等。

(1) NRZ码

NRZ码是用信号的幅度来表示二进制数据的,通常用正电压表示数据“1”,用负电压表示数据“0”,并且在表示一个码元时,电压均无需回到零,故称不归零码。NRZ码的特点是一种全宽码,即一位码元占一个单位脉冲的宽度。全宽码的优点:一是每个脉冲宽度越大,发送信号的能量就越大这对于提高接收端的信噪比有利;二是脉冲时间宽度与传输带宽成反比关系,即全宽码在信道上占用较窄的频带,并且在频谱中包含了码位的速度。

NRZ码的主要缺点是:当数据流中连续出现0 或1时,接收端很难以分辨1个信号位的开始或结束,必须采用某种方法在发送端和接收端之间提供必要的信号定时同步。同时,这种编码还会产生直流分量的积累问题,这将导致信号的失真与畸变,使传输的可靠性降低,并且由于直流分量的存在,使得无法使用一些交流耦合的线路和设备。因此,一般的数据传输系统都不采用这种编码方式。

(2) DNRZ码

DNRZ码是一种NRZ码的改进形式,它是用信号的相位变化来表示二进制数据的,一个信号位的起始处有跳变表示数据“1”,而无跳变表示数据“0”。DNRZ码不仅保持了全宽码的优点,同时提高了信号的抗干扰性和易同步性。

近年来,越来越多的高速网络系统采用了DNRZ码,成为主流的信号编码技术,在FDDI、100BASE-T及100VG-AnyLAN等高速网络中都采用了DNRZ编码。其原因是在高速网络中要求尽量降低信号的传输带宽,以利于提高传输的可靠性和降低对传输介质带宽的要求。而DNRZ编码中的码元速率与编码时钟速率相一致,具有很高的编码效率,符合高速网络对信号编码的要求。同时,为了解决数据流中连续出现0 或1时所带来的信号编码问题,通常采用两级编码方案,第一级是预编码器,对数据流进行预编码,使编码后的数据流不会出现连续 0 或连续 1,常用的预编码方法有4B5B、5B6B等;第二级是DNRZ编码,实现物理信号的传输。这种两级编码方案的编码效率可达到 80%以上。例如,在4B5B编码中,每4位数据用5位编码来表示,即4位数据就会增加 1 位的编码开销,编码效率仍为80%。

(3) 曼彻斯特码

在曼彻斯特码中,用一个信号码元中间电压跳变的相位不同来区分数据“1”和“0”,它用正的电压跳变表示“0”;用负的电压跳变表示“1”。因此,这种编码也是一种相位码。由于电压跳变都发生在每一个码元的中间,接收端可以方便地利用它作为位同步时钟,因此这种编码也称为自同步码。10Mb/s 以太网(Ethernet)采用这种曼彻斯特码。

(4) 差分曼彻斯特码

差分曼彻斯特码是一种曼彻斯特码的改进形式,其差别在于:每个码元的中间跳变只作为同步时钟信号;而数据“0”和“1”的取值是用信号位的起始处有无跳变来表示,若有跳变则为“0”;若无跳变则为“1”。这种编码的特点是每一位均用不同电平的两个半位来表示,因而始终能保持直流的平衡。这种编码也是一种自同步编码。
令牌环(Token-Ring)网采用这种差分曼彻斯特编码。

这两种曼彻斯特编码主要用于中速网络(Ethernet为 10 Mb/s;Token-Ring最高为16 Mb/s)中,而高速网络并不采用曼彻斯特编码技术。其原因是它的信号速率为数据速率的两倍,即对于 10 Mb/s的数据速率,则编码后的信号速率为 20 Mb/s,编码的有效率为 50%。对于 100 Mb/s的高速网络来说,200 Mb/s的信号速率无论对传输介质的带宽的要求,还是对传输可靠性的控制都未免太高了,将会增加信号传输技术的复杂性和实现成本,难以推广应用。因此,高速网络主要采用两级的DNRZ编码方案,而中速网络采用曼彻斯特编码方案,尽管它增加了传输所需的带宽,但在实现起来简单易行。

要实现手机端和扩展头的全双工通信,必须满足2个条件:1.信号必须在音频频率之内;2.需要是低功耗的。第一个条件限制了信号带宽,第二个条件限制了成本和功率。在这2种限制条件下,主要有2种方式实现这种同时的双向通信:FSK调制和基于曼彻斯特编码的直接数字通信。

这里主要讲解下通过数字调制中的FSK调频调制方波。FSK有2FSK(2进制调制)、4FSK(4进制调制)、8FSK(8进制调制)等等。

由于在数字系统中,使用的是0、1表示的二进制数据,在这里,我使用了2FSK来作为信号的调制。

2FSK调频调制方波的原理:用一个频率表示1,另一个不同的频率表示0。比如使用613Hz的信号代表0,1226Hz的信号代表1。如下图所示


方波可以用Cool Edit Pro工具生成,方便调试和研究。具体方法是:
打开工具,新建文件,然后会弹出下面窗口


   这里根据你需求设置波形的采样率,声道和采样精度(一般是44100HZ,单声道,16位采样精度)

接下来选择菜单项-生成

你会看到如下界面,这里可以通过设置不同频率和设置调味为方波生成波形了


方波

那么在iOS下如何接收方波解析和发送方波咧?

每段波形其实是由N(N=采样率/位持续时间(每位数据频率))个有符号的数据点组成,这些有正有负的数组成了一段段波形,比如波形0则有N个127组成的波峰波形或者N个-128组成的波底波形,而波形1则有N/2个波峰+N/2个波低组成,这个波峰波底具体数值定义可自已灵活定义,如下图所示

波形组成原理

举个例子:比如发送个数据5,其二进制数据为0000 0101 ,这里设置采样率为44100HZ,每段波形时间频率1000HZ,定义波形为0 波峰上全是44个127,波形为0 波底上全是44个-128,波形为1的数据 半个 波峰上全是44/2个127,半个 波底上全是44/2个-128,那么5 生成波形的数据就是 0(44个127) 0 (44个-128) 0(44个127) 0 (44个-128) 0(44个127)1(44/2个-128+44/2个127)0(44个-128)1(44个127)。知道这个原理了方波解析和发送数据就简单了。

音频使用的基本步骤如下:

1.设置音频格式和采样率

mAudioFormat.mSampleRate=AUDIO_SAMPLE_RATE;//采样率
mAudioFormat.mFormatID=kAudioFormatLinearPCM;//PCM格式
mAudioFormat.mFormatFlags        = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
mAudioFormat.mFramesPerPacket    = 1;//每个数据包多少帧
mAudioFormat.mChannelsPerFrame   = kChannels/2;//1单声道,2立体声
mAudioFormat.mBitsPerChannel     = 16;//语音每采样点占用位数
mAudioFormat.mBytesPerFrame      = mAudioFormat.mBitsPerChannel*mAudioFormat.mChannelsPerFrame/8;//每帧的bytes数
mAudioFormat.mBytesPerPacket     = mAudioFormat.mBytesPerFrame*mAudioFormat.mFramesPerPacket;//每个数据包的bytes总数,每帧的bytes数*每个数据包的帧数
mAudioFormat.mReserved           = 0;

2.设置remote io unit的渲染回调,从输入硬件获得采样传入到回调函数进行渲染,从而获得录音数据解析数据. 向回调函数填数据,从而向输出硬件提供数据进行放音发送数据.
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");

回调函数OSStatus RenderTone(
                void *inRefCon,
                AudioUnitRenderActionFlags  *ioActionFlags,
                const AudioTimeStamp        *inTimeStamp,
                UInt32                      inBusNumber,
                UInt32                      inNumberFrames,
                AudioBufferList             *ioData);通过这个回调函数就可以发送数据和解析数据

数据编码发送具体代码可参考demo里,知道了编码原理,解码对你来说就简单了

参考文档:
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

推荐阅读更多精彩内容