關於手機音頻通信實際開發經驗分享


轉:關於手機音頻通信實際開發經驗分享



一、手機音頻通信的特點
1、        通用性強:在智能手機普及的今天,手機的對外通信接口多種多樣,而其中以3.5mm的音頻接口通用新最強,基本所有的手機、平板電腦都會有這個接口,所以在一些要求通用性的設備上,音頻接口登上了舞臺。
2、        速率低:由於手機音頻部分的採樣頻率一般爲44.1KHZ(部分國產山寨爲8KHZ),這極大的限制了音頻通訊的速率。我們都知道44.1KHZ的採樣頻率,那麼最高的信號頻率只能爲20KHZ左右,而信號週期也不可能只有2個採樣點,通常要到10個以上,這樣層層下來通訊速率可想而知。
3、        小信號:音頻通信的信號都是毫伏級的,各個手機廠商略有不同,但通常最大不超過200mv,通常我們通信使用的信號強度也就100mv左右,這導致信號比較容易受干擾,且在開發階段對工具有着種種限制。
二、        手機音頻通信分類
1、        無線方式:
a)        無線方式大家可能不太熟悉,容我慢慢道來。我們都知道人耳能聽到的聲音頻率爲20HZ~20KHZ,而手機通信的信號頻率最高也就20KHZ,所以無線通信方式是可行的。因爲雖然人耳的極限聽力能到20KHZ,但普通人一般在19KHZ以上時基本就聽不到了,所以如果信號的強度比較弱,且控制在19KHZ到20KHZ之間,那麼我們就可以將之當做是“超聲波”來看待了。
b)        其實在此提到手機音頻通信的無線方式,算是給大家一種產品開發思路吧。它的通訊半徑在10M左右,前景還是很廣闊的,大家有興趣的可以試試。(其實已經有這方面的產品了)
2、        有線方式:
a)        有線方式分爲單向(設備→手機)和雙向兩種,單向的限制少,開發難度也小一些,但實際應用時會受限制。而雙向通信限制多,開發難度也大一些,但實際應用時更方便些。
b)        設備→手機:曼徹斯特編碼;FSK;DTMF;自定義正弦波
c)        手機→設備:由於手機輸出的音頻信號很小,無法直接使用,要麼用運放發大到合適的範圍,要麼用電壓比較器轉換成TTL方波。
三、手機音頻通信硬件通信方式分類:手機音頻通信的硬件通信方式大體可分爲方波和正弦波兩種。
1、        方波:方波通常使用的是曼徹斯特編碼方式(什麼是曼徹斯特編碼自己去查),它的好處是可以用單片機直接輸出方波,經過衰減後即可使用,方便簡單。缺點是兼容性不好,因爲手機音頻部分有這樣一個特性,它只識別變化的電平信號,當麥克輸入的信號長時間保持在某一非零電平時,手機會將其視爲零,而強行拉回零電位。這就是採用方波通訊方式的兼容性不好的最大原因了,並且方波也容易受干擾。
2、        正弦波:正弦波不會出現上面所說的方波的問題,故正弦波的兼容性和穩定性更好一些。通常採用方案有FSK、DTMF、信號發生器、或方波轉正弦波等。(後面會對以上方案逐一分析)
3、        通信信道分析
a)        我們知道音頻接口有4根線,MIC、地、左、右聲道。設備→手機用MIC,手機→設備用地、左、右聲道中的任意一個。這裏說一下,實際產品中,有一些廠家會更換地線,即將原本左、有聲道中的一根改爲地線來用,其實道理是一樣的。因爲音頻通信的信號時交流信號,而地其實也是懸浮地,即便地線換了,最終的波形還是一樣的,因爲最終手機解析信號時需要的是頻率和幅值。這樣還剩下一個聲道,通常被用來幫助設備進行上電識別,因爲音頻通信的設備通常都是電池供電的。
b)        另外還要在MIC和地之間並聯一個4.99K的電阻,因爲手機是通過檢測MIC和地之間的阻抗是否爲4.99K(也有其他阻值的)來判斷是否有設備(耳機)插入,這一點要謹記。
四、各個通信方案對比分析
1、        設備→手機:
a)        曼徹斯特編碼:在諸多通信方式中,曼徹斯特編碼是最靈活簡便的一種方法,編碼信號可由單片機直接產生,經衰減電路衰減後便可直接使用。注意事項:曼徹斯特編碼信號的生成有兩種方式,一種是用PWM生成,一種是用定時器中斷翻轉IO,我個人比較傾向於定時器中斷方式。因爲我們知道曼徹斯特編碼中有寬沿河窄沿之分,且寬沿和窄沿可能會靈活變化,而用PWM方式不容易精確控制寬沿、窄沿輸出的變化,而定時器中斷方式則非常靈活且容易控制。(後面會送上我自己寫的曼徹斯特編碼、解碼函數)
b)        FSK、DTMF方式:FSK和DTMF兩種方式大同小異,使用時通常都是用集成的芯片來生成的,而這些芯片通常都是遵守固定的通信協議的的要求(FSK爲Bell202或V.23協議,DTMF記不清名字了)。這兩種通信方式的優點是採用正弦波通信、穩定性好且使用簡便。但由於固定通信協議的限制導致通信速率、比特率也受到限制而缺乏靈活性。在這裏跟他家推薦一款英國的通信芯片CMX系列,這個系列的芯片融合的FSK、DTMF的編碼、解碼,還是很不錯的,大家有興趣可以試試。(相關手冊在附件裏)
c)        信號發生器、鎖相環方式:這種方式用信號發生器或者鎖相環來產生方波或正玄波,由單片機來控制波形的輸出,也可以實現音頻通信,且十分靈活。但缺點是電路較複雜,且不同頻率信號之間銜接不好掌握,用不好反而是麻煩。(相關手冊在附件裏)
d)        在這裏送上一種我個人認爲比較好的方案:就是曼徹斯特編碼加低通濾波器,由單片機輸出曼徹斯特編碼,再經由低通濾波器將方波濾成正弦波後輸出。既解決了FSK、DTMF靈活性的問題,又解決了曼徹斯特編碼方波穩定性、通用性的問題。在低通濾波器方面我個人採用的是“集成低通開關電容濾波器”,它成本雖然高一些,但好處也是明顯的,電路簡單,使用方便,且佔用的空間亦很小。(相關手冊在附件裏)
2、        手機→設備:
a)        放大電路方式:將手機輸出信號經放大電路放大到合適的幅值,然後有鎖相環或者結成FSK、DTMF芯片進行解析。該中方式難度最大,需要非常強的模擬電路功底,我個人水平有限,故採用的另一種方式。
b)        電壓比較器方式:將手機輸出的交流信號經電路強行拉到Vcc/2級別,然後加到電壓比較器一端,另一段接比較電壓Vcc/2,這樣交流信號即被轉化爲TTL方波信號,此時再進行解析就變得很簡單了。
五、研發注意事項(通訊方案分析部分由於過長,放到最後來講)
1、        一個好手機錄音軟件是必須的,最好能在手機上直接看到波形的。
2、        建議用筆記本電腦進行開發,而非臺式機。因爲音頻信號很小,容易受干擾,而臺式機干擾較大,筆記本還有一個好處是必要時可將外接電源拔掉,用電池供電。
3、        一個好錄音筆必不可少,有時需要得到純淨的音頻信號,方便更加準確的分析。
4、        做一個轉接板,一邊接音頻母座,一邊接音頻公頭,將MIC、地、左、右聲道4跟線用排陣引出,方便錄音。
5、        做一個信號衰減電路,可將設備電路產生的信號衰減至音頻接口能承受的範圍內。前期調試時,我們可以用該電路將信號錄進電腦進行信號分析。(推薦一個電腦音頻信號分析軟件:Goldwave)
6、        錄音用的音頻線切記不要太長,不然會給你帶來不少麻煩。最好自己做,用音頻裸頭、杜邦線、排陣即可製作,方便好用。

曼徹斯特編碼的編碼解碼函數如下:
  1. /**********************************************************************
  2. 註釋:編碼函數都是採用定時器中斷的形式,以曼徹斯特編碼的窄沿作爲定時器週期。
  3. 發送的數據包括1個起始位、8個數據位、1個奇偶校驗位、3個停止位。
  4. ***********************************************************************/
  5. static void VIC_VECT_Fucton_00(void)//發送編碼數據中斷函數
  6. {
  7.         TIMER0IS =0x0;
  8.         if((send_time%2==0) && (send_start==1))
  9.         {
  10.                 switch(FSK_txState)
  11.         {
  12.           case STARTBIT:
  13.                 if((GPIODATA&0x00000002)==0x00000000)//如果檢測到數據發送管腳爲零
  14.                         send_time++;
  15.                         else
  16.                         {
  17.                                 currentSym=0;
  18.                                 FSK_txState = BYTE;
  19.                         }
  20.                         break;
  21.           case BYTE:
  22.             if(txBit < 8)
  23.             {
  24.                     currentSym = (send_byte >> txBit) & 0x01;
  25.                 txBit++;
  26.                 txParity += currentSym;                  //奇偶校驗位
  27.             } 
  28.                         else if (txBit == 8)
  29.             {
  30.                     currentSym = txParity & 0x01; //發送奇偶校驗位
  31.                 txBit++;
  32.             } 
  33.                         else if(txBit>8 && txBit<12)
  34.             {
  35.                     // next bit is the stop bit
  36.                 currentSym = 1;                                  //發送停止位
  37.                                 txBit++;
  38.             }
  39.                         else if(txBit == 12)
  40.                         FSK_txState = STOPBIT;        
  41.             break;
  42.           case STOPBIT :
  43.             txBit=0;
  44.                         FSK_txState=IDLE;
  45.                         send_start=0;
  46.                         txParity=0;
  47.                         send_byte=0;
  48.                         break;
  49.                }
  50.                 if(lastSym!=currentSym)
  51.                 {
  52.                         timer1_num++;
  53.                         lastSym=currentSym;
  54.                 }
  55.         }
  56.         if(timer1_num%2==0)
  57.         GPIODATA&=0xFFFFFFFD;//輸出管腳復位
  58.         else
  59.         GPIODATA|=0x00000002;//輸出管腳置位

  60.         timer1_num++;//用來控制IO口的電平翻轉
  61.         send_time++;//用來控制發送的字節的每一位
  62.         Delay++;//Delay就是延時函數
  63. }
  64. /**********************************************************************
  65. 註釋:解碼函數採用外部IO中斷形式(上升沿或下降沿中斷,即電平電平跳變中斷),
  66. 用一個定時器作爲時鐘,每次產生中斷時便從定時器見時間值取出,並和上一次的
  67. 記錄做差求出時間間隔,以此來判斷當前爲寬沿還是窄沿。
  68. ***********************************************************************/
  69. static void VIC_VECT_Fucton_04(void)//接受解碼數據中斷函數
  70. {
  71.         GPIOIC|=0x00000001;//清楚上一次中斷內容
  72.         RX_time=TIMER1VALUE;
  73.         if(RX_lasttime>=RX_time)
  74.         RX_diff=RX_lasttime-RX_time;                  //lasttime初始值爲0
  75.         else
  76.         RX_diff=65535-RX_time+RX_lasttime;
  77.         RX_lasttime=RX_time;        
  78.     switch(RX_state)                  //啓動代碼時state已經被配置爲STARTBIT
  79.     {
  80.           case STARTBIT_FALL:
  81.             if ((SHORTINTERVAL<RX_diff) && (RX_diff<LONGINTERVAL))
  82.         {
  83.                 if(RX_ones<5)           //ones初始值爲0
  84.             {
  85.                     RX_ones = 0;
  86.             } 
  87.                         else 
  88.                         {
  89.                                 RX_state = DECODE;        //將狀態配置爲解碼
  90.             }
  91.         }
  92.         else if(RX_diff < SHORTINTERVAL)
  93.         RX_ones++;
  94.                 else
  95.                 RX_ones=0;
  96.         break;
  97.       case DECODE:
  98.               /**************通過間隔長短來判定數據**************/
  99.                 if ((SHORTINTERVAL<RX_diff) && (RX_diff<LONGINTERVAL))// 若間距在範圍內則當前數據位值和前一個相反
  100.         {                                                
  101.                         currentbit=(currentbit+1)&0x01;
  102.                         RX_times+=2;
  103.         }
  104.         else if( RX_diff < SHORTINTERVAL)                                         
  105.         {
  106.                         currentbit=currentbit;
  107.                         RX_times++;
  108.                 }
  109.         else
  110.                 RX_state = DATAINIT;                           

  111.                 /****************接受數據位,從低位接起****************/
  112.                 if(RX_times%2==0)
  113.                 {
  114.                         if(RX_bitcounter<8)
  115.                         {
  116.                                 if (currentbit==1)
  117.                             {
  118.                                       uartByteRx = (uartByteRx >> 1) + (1<<7);
  119.                                         rxParity++;                                                //奇偶校驗位
  120.                                         RX_bitcounter++;                                //接受數據位數
  121.                             }
  122.                             else
  123.                             {
  124.                                         uartByteRx = (uartByteRx >> 1);
  125.                                         RX_bitcounter++;
  126.                             }
  127.                         }
  128.                         else
  129.                         {
  130.                                 rxParity&=0x01;                                                 //進行奇偶校驗
  131.                                 if(rxParity==currentbit)
  132.                                 {
  133.                                         RX_bitcounter++;
  134.                                         RX_finish=1;
  135.                                         RX_state=DATAINIT;
  136.                                 }
  137.                                 else
  138.                                 RX_state=DATAINIT;                                         //若奇偶校驗錯誤則,重新檢測
  139.                         }
  140.                 }
  141.                 break;
  142.       case DATAINIT :                                                                 //初始化參數狀態
  143.                 RX_bitcounter=0;
  144.                 RX_ones=0;
  145.                 rxParity=0;
  146.                 currentbit=0;
  147.                 RX_state=STARTBIT_FALL;
  148.                 RX_times=0;
  149.                 break;
  150.       default:
  151.         break;
  152.     }
  153. }
複製代碼




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章