乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      [智能硬件] 2、三分鐘看懂智能硬件原理

       看見就非常 2015-11-20

       

        

        恭喜大家順利通過測試一!在測試一中我們學(xué)會(huì)了如何利用現(xiàn)有模塊HC-05/06進(jìn)行簡單的連線來制作一個(gè)藍(lán)牙防丟器,同時(shí)學(xué)習(xí)了安卓藍(lán)牙相關(guān)的幾個(gè)API并最終制作了一個(gè)自己的藍(lán)牙防丟客戶端軟件??赡苡行iT來看軟硬結(jié)合的同學(xué)會(huì)抱怨“什么呀,感覺就是在開發(fā)安卓App嘛!“。不錯(cuò)!測試一的目的就是讓大家通過了解硬件原理DIY一個(gè)簡單的硬件,并學(xué)習(xí)如何充分利用移動(dòng)端開發(fā)的特點(diǎn)設(shè)計(jì)一款配套的應(yīng)用。除此之外樓主還悄悄地為測試二埋下了伏筆,因?yàn)闇y試二將會(huì)涉及利用移動(dòng)端和藍(lán)牙模塊的通信功能來實(shí)現(xiàn)一個(gè)遙控小風(fēng)扇!如果沒有前面關(guān)于藍(lán)牙軟硬件知識的鋪墊,直接做這個(gè)可能會(huì)很吃力。那么現(xiàn)在我們就著手測試二吧![正版請搜索:beautifulzzzz(看樓主博客園官方博客,享高質(zhì)量生活)嘻嘻?。?!]

       

      1 預(yù)期效果構(gòu)思

        簡單起見我們實(shí)現(xiàn)一個(gè)可以通過手機(jī)App遙控的可調(diào)速小風(fēng)扇。如圖1_1左邊手機(jī)應(yīng)用部分主要包括1、2、3三個(gè)按鈕和4用于顯示風(fēng)扇速度的文本框;右邊小風(fēng)扇部分主要包括7風(fēng)扇模塊和8用于顯示風(fēng)扇速度的顯示模塊;中間的5、6表示雙方通過藍(lán)牙進(jìn)行無線通信實(shí)現(xiàn)遙控功能。

                   圖1_1 預(yù)期效果構(gòu)思

       

      2 硬件輪廓勾勒

            其實(shí)整個(gè)硬件部分都是要我們自己DIY的。如圖2_1所示1號為51最小系統(tǒng)模塊,起總控作用;2號為電源模塊,用于向整個(gè)系統(tǒng)供電;3號為藍(lán)牙模塊,用于單片機(jī)和智能手機(jī)進(jìn)行藍(lán)牙通信;4號為電機(jī)模塊(包括電機(jī)驅(qū)動(dòng)電路),用于將電能轉(zhuǎn)換為機(jī)械能提供風(fēng);5號為數(shù)碼管顯示模塊,用于顯示小風(fēng)扇的當(dāng)前轉(zhuǎn)速。

          

                    圖 2_1 硬件輪廓勾勒

       

      3 硬件整體電路圖設(shè)計(jì)

        既然輪廓已經(jīng)勾勒出,接下來要看看我們具體需要哪些元件。首先對于51最小系統(tǒng)模塊(如圖3_1所示)包括晶振電路和89C52單片機(jī)(其實(shí)為了簡單筆者偷偷地將復(fù)位電路去掉了,這樣帶來的直接后果是程序燒不進(jìn)去。如果大家也一樣學(xué)著偷懶,不妨把該最小系統(tǒng)的電源引腳和串口引腳用杜邦線連接到你買來的開發(fā)板對應(yīng)的引腳處,同時(shí)把開發(fā)板上的單片機(jī)拿掉。這樣就可以利用開發(fā)板上的復(fù)位電路模塊來實(shí)現(xiàn)程序的有效燒寫。)

       

                   圖 3_1 51最小系統(tǒng)

        對于電源模塊,我們可以使用可充電的5V鋰電池或者用3節(jié)1.5V的普通電池湊合。藍(lán)牙模塊是我們上一章中制作藍(lán)牙防丟器的HC-05或HC-06。這里電機(jī)模塊要特別說明下:如圖3_2需要用一個(gè)ULN2003做驅(qū)動(dòng),這樣控制信號要從4號引腳輸入以實(shí)現(xiàn)對馬達(dá)的控制。另外馬達(dá)可以選擇玩具四驅(qū)車上的那種。

          

                  圖 3_2 電機(jī)模塊

        最后顯示模塊采用的是四位八段共陰數(shù)碼管3461AS。如圖3_3每個(gè)3461AS有4個(gè)數(shù)碼管,每個(gè)數(shù)碼管中有8個(gè)LED燈。這樣當(dāng)我們想使某一個(gè)數(shù)碼管顯示相應(yīng)的數(shù)字時(shí),只要給4路位選信號和8路段選信號相應(yīng)的組合電平就能實(shí)現(xiàn)功能。需要另外說明的是:3461AS屬于共陰數(shù)碼管,如圖3_4其中6、8、9、12為位選引腳,3、5、10、1、2、4、7、11為段選引腳。如果我們想讓第二個(gè)數(shù)碼管顯示2時(shí),要讓9號引腳置低電平其余位選引腳置高電平,同時(shí)要讓11、7、5、1、2置高電平其余段選置低電平。

                  圖 3_3 3461AS封裝圖

                  圖 3_4 3461AS內(nèi)部電路圖

        因此在實(shí)際電路中(如圖3_5)將P0口作段選信號引腳,同時(shí)用P2.3、P2.4、P2.5、P2.6作為位選信號引腳,通過單片機(jī)直接驅(qū)動(dòng)即可。此外R1~R8八個(gè)上拉電阻不能忽視,起初筆者沒有注意結(jié)果燒壞了2個(gè)3461AS。

        

                  圖3_5 顯示模塊實(shí)際驅(qū)動(dòng)電路

        最終我們設(shè)計(jì)的電路圖如下,其中RXD和TXD引腳接HC-05或HC-06的TXD和RXD(要交錯(cuò)相連)。因?yàn)镠C-05/06是藍(lán)牙串口模塊,也就是說只要單片機(jī)采用串口驅(qū)動(dòng)程序并且相應(yīng)的引腳連接正確,單片機(jī)-藍(lán)牙模塊通信完全和單片機(jī)-串口設(shè)備通信一樣。所以圖中的串口模塊也就相當(dāng)于我們的藍(lán)牙模塊,唯一需要注意的是單片機(jī)和藍(lán)牙模塊的RXD和TXD是交錯(cuò)相連。

                                  圖 3_6 整體電路圖

       

      4 四位八段共陰數(shù)碼管3461AS的驅(qū)動(dòng)程序設(shè)計(jì)

        由上面分析我們知道通過位選信號和段選信號的組合可以實(shí)現(xiàn)數(shù)碼管顯示功能。如果采用圖3_6所示電路圖,上面想讓第二個(gè)數(shù)碼管顯示2時(shí),則P0等于0x5b(01011101),P2等于0xdf(11011111)。采用同樣的分析方法我們可以計(jì)算出讓八段數(shù)碼管顯示從0~F的所有P0對應(yīng)的賦值:0x3f 0x06 0x5b 0x4f 0x66 0x6d 0x7d 0x07 0x7f 0x6f 0x77 0x7c 0x39 0x5e 0x79 0x71,以及單獨(dú)選通第1位到第4位P2的所有賦值:0xbf 0xdf 0xef 0xf7。這樣當(dāng)我們想讓第3位顯示9只需要給P0、P2分別賦值0x6f和0xef即可。

        這時(shí)大家可能會(huì)有這樣的疑惑:“按照上面的說法似乎每次只能讓某一位顯示一個(gè)數(shù)字”。其實(shí)有這樣的疑惑說明大家學(xué)的比較認(rèn)真,其實(shí)生活中很多數(shù)碼管的顯示案例中都是每次只顯示一位的!之所以我們看到的情況是一次顯示多個(gè),就在于數(shù)碼管驅(qū)動(dòng)程序設(shè)計(jì)了!而這其中的秘訣則是采用了高頻刷新(也即動(dòng)態(tài)掃描)這一技巧。如果大家對動(dòng)態(tài)掃描沒有感覺,可以想象一下?lián)]舞熒光棒時(shí)的樣子——本來只是一根熒光棒,由于揮舞速度比較快而在空中劃出一道美麗的弧線。下面結(jié)合驅(qū)動(dòng)程序和大家詳細(xì)介紹:

      復(fù)制代碼
       1 #include"display_4X8.h"
       2 
       3 unsigned char code DuanMa[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
       4       0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};// 顯示段碼值0~F
       5 unsigned char code WeiMa[]={0xbf,0xdf,0xef,0xf7};//分別對應(yīng)相應(yīng)的數(shù)碼管點(diǎn)亮,即位碼
       6 unsigned char TempData[4]; //存儲(chǔ)顯示值的全局變量
       7 
       8 //------------------------------------------------
       9 //4位8段共陰數(shù)碼管顯示函數(shù)
      10 //第一個(gè)參數(shù)為0表示從第一個(gè)數(shù)碼管開始顯示num個(gè)數(shù)
      11 //提前要顯示的數(shù)要存在TempData中(TempData[0]表示要顯示的第一個(gè)數(shù))
      12 //------------------------------------------------
      13 void Display(unsigned char FirstBit,unsigned char Num)
      14 {
      15     static unsigned char i=0;
      16 
      17     DataPort=0x00;   //清空數(shù)據(jù),防止有交替重影
      18     DataControl=0x00;
      19 
      20     DataPort=TempData[i]; //取顯示數(shù)據(jù),段碼
      21     DataControl=WeiMa[i+FirstBit];
      22 
      23     i++;
      24     if(i==Num)
      25         i=0;
      26 }
      復(fù)制代碼

        這里的DuanMa[]和WeiMa[]不再說明,TempData[4]用來存儲(chǔ)要顯示數(shù)據(jù)。在該驅(qū)動(dòng)中只有一個(gè)Display函數(shù),正如第10行提示所述:第一個(gè)參數(shù)用來表明從哪一個(gè)數(shù)碼管開始顯示數(shù)據(jù),第二個(gè)參數(shù)表明一共要顯示多少位數(shù)據(jù)。這樣如果要在數(shù)碼管的后兩位顯示一個(gè)兩位數(shù)則可以用Display(2,2)。這里要特別說明下TempData數(shù)組,該數(shù)組用于存放數(shù)碼管要顯示的數(shù)據(jù),千萬不要把該數(shù)組和數(shù)碼管直接對應(yīng)。例如同樣是在數(shù)碼管后兩位顯示一個(gè)兩位數(shù)num可以采用下列兩種方案:

         ① TempData[0]=DuanMa[num/10];
         TempData[1]=DuanMa[num%10];
         Display(2,2);
        ② TempData[2]=DuanMa[num/10];
          TempData[3]=DuanMa[num%10];
          Display(0,4);

        其中方案一直接把要顯示的兩位數(shù)據(jù)存儲(chǔ)在TempData的前兩位,然后調(diào)用Display函數(shù)從第3個(gè)數(shù)碼管開始顯示2位來實(shí)現(xiàn)功能。方案二其實(shí)是把要顯示的數(shù)據(jù)存放在TempData的后兩位(前兩位默認(rèn)為0),然后調(diào)用Display函數(shù)從第1個(gè)數(shù)碼管開始顯示4位來實(shí)現(xiàn)功能。

        對于動(dòng)態(tài)掃描這里用了一個(gè)很巧妙的方法:注意到第15行定義了一個(gè)靜態(tài)變量i,其功能在于實(shí)現(xiàn)一個(gè)周期內(nèi)實(shí)現(xiàn)對需要點(diǎn)亮的數(shù)碼管順序點(diǎn)亮。這樣如果Display(0,4)顯示1234,則數(shù)碼管的慢動(dòng)作則為:第一個(gè)數(shù)碼管顯示1、接著第二個(gè)數(shù)碼管顯示2、然后第三個(gè)數(shù)碼管顯示3……由于刷新頻率很高,所以人眼看上去就是4個(gè)數(shù)碼管同時(shí)顯示1234的效果。

       

      5 PWM實(shí)現(xiàn)變速小馬達(dá)

        欲實(shí)現(xiàn)直流小馬達(dá)的速度控制這里必須先講解下PWM。所謂PWM是“Pulse Width Modulation”的縮寫,簡稱脈寬調(diào)制。它是利用微處理器的數(shù)字輸出來對模擬電路進(jìn)行控制的一種非常有效的技術(shù)。這里舉個(gè)通俗的例子來解釋PWM:假設(shè)你是某公司的老板,手下有個(gè)奇葩的員工喜歡周期性的在一個(gè)小時(shí)內(nèi)干一會(huì)休息一會(huì),如果你想多壓榨一下他就會(huì)督促讓他在一個(gè)周期內(nèi)多干活少休息。同樣的利用微處理器在一個(gè)比較短的周期內(nèi)設(shè)置某個(gè)引腳輸出高電平比低電平的持續(xù)時(shí)間多一點(diǎn),從宏觀上看則呈現(xiàn)出輸出功率升高的效果,反之輸出功率變低。

                圖 5_1不同占空比的輸出脈沖

       

      6 串口驅(qū)動(dòng)程序設(shè)計(jì)

        上面已經(jīng)介紹過單片機(jī)和藍(lán)牙模塊的通信方式是采用串口通信,其重要特別注意的是單片機(jī)和HC-05/06的RXD引腳和TXD引腳要交錯(cuò)相連。既然HC-05/06采用的是串口通信方式,所以在給單片機(jī)編程時(shí)只要按照串口驅(qū)動(dòng)來設(shè)計(jì)就可以了。

      復(fù)制代碼
       1 #include"uart.h"              
       2 
       3 #define Length 8
       4 
       5 unsigned char getByte[Length];  //定義臨時(shí)變量
       6 unsigned char flag;             //接收標(biāo)記
       7 unsigned char point;            //指針
       8 
       9 //------------------------------------------------
      10 //串口初始化
      11 //------------------------------------------------
      12 void InitUART  (void)
      13 {
      14     flag=0;
      15     point=0;
      16     SCON  = 0x50;                // SCON: 模式 1, 8-bit UART, 使能接收  
      17     TMOD |= 0x20;               // TMOD: timer 1, mode 2, 8-bit 重裝
      18     TH1   = 0xFD;               // TH1:  重裝值 9600 波特率 晶振 11.0592MHz  
      19     TL1   = 0xFD;   
      20     TR1   = 1;                  // TR1:  timer 1 打開                         
      21     EA    = 1;                  //打開總中斷
      22     ES    = 1;                  //打開串口中斷
      23 }       
      24                      
      25 //------------------------------------------------
      26 //發(fā)送一個(gè)字節(jié)
      27 //------------------------------------------------
      28 void SendByte(unsigned char dat)
      29 {
      30     SBUF = dat;
      31     while(!TI);
      32     TI = 0;
      33 }
      34 
      35 //------------------------------------------------
      36 //發(fā)送一個(gè)字符串
      37 //------------------------------------------------
      38 void SendStr(unsigned char *s)
      39 {
      40     while(*s!='\0')// \0 表示字符串結(jié)束標(biāo)志,通過檢測是否字符串末尾
      41     {
      42         SendByte(*s);
      43         s++;
      44     }
      45 }
      46 
      47 //------------------------------------------------
      48 //串口中斷程序
      49 //------------------------------------------------
      50 void UART_SER (void) interrupt 4 //串行中斷服務(wù)程序
      51 {
      52     if(RI)                                  //檢測接收完成標(biāo)志位置1
      53     {
      54         RI=0;                            //清零接收完成標(biāo)志位
      55         getByte[point]=SBUF;               //讀取接收到的數(shù)據(jù)
      56 
      57         if(getByte[point++]==0xAA)        //遇到可能的結(jié)束標(biāo)志則發(fā)送flag
      58             flag=1;                        //再主函數(shù)再進(jìn)行判斷是否為有效幀
      59 
      60         if(point==8)                    //防止數(shù)組越界
      61             point=0;
      62     }
      63 }
      復(fù)制代碼

        在該串口驅(qū)動(dòng)文件里主要包括串口初始化函數(shù)InitUART,用來設(shè)置串口通信的波特率和接收中斷等。接下來分別是發(fā)送一字節(jié)函數(shù)和發(fā)送一個(gè)字符串函數(shù)。這里單片機(jī)向串口設(shè)備發(fā)送信息采用直接發(fā)送,即在程序中用到要發(fā)送信息的地方直接調(diào)用發(fā)送函數(shù)發(fā)送;但是數(shù)據(jù)接收則采用中斷的方式,因?yàn)樵陧樞驁?zhí)行的程序中不容易處理隨時(shí)都可能傳輸過來的信息。在中斷函數(shù)中把每次接收來的數(shù)據(jù)保存在getByte數(shù)組中。由于這里采用了數(shù)據(jù)幀,所以包含了對數(shù)據(jù)有效性的驗(yàn)證,這個(gè)將在下面詳細(xì)分析。

       

      7 硬件工程整體介紹

      1) 打開Keil uVision2,點(diǎn)擊Project下的Open Project,打開智能小風(fēng)扇.Uv2加載工程。

                      圖 7_1 打開工程

      2) 待工程加載完畢,大家會(huì)在工程窗口中看到圖7_2所示文件結(jié)構(gòu)。其中FUNC組下面包含數(shù)碼管顯示驅(qū)動(dòng)和串口驅(qū)動(dòng)文件,INTE組下包含中斷相關(guān)文件,USER組下是最上層應(yīng)用程序文件。

                 

                      圖 7_2 文件結(jié)構(gòu)

      3) 之前采用的思路是從底向上設(shè)計(jì),這次將采用從上向下講解工程。首先看USER組下的main.c文件:

      復(fù)制代碼
       1 #include "../FUNC/display_4X8.h"
       2 #include "../FUNC/uart.h"
       3 #include "../INTE/inte.h"
       4 
       5 sbit DCOUT = P1^1;//定義電機(jī)信號輸出端口
       6 //------------------------------------------------
       7 //全局變量
       8 //------------------------------------------------
       9 unsigned char PWM_ON;   //定義速度等級
      10 #define CYCLE 10        //周期
      11 
      12 //變量
      13 extern unsigned char code DuanMa[];// 顯示段碼值
      14 extern unsigned char TempData[]; //存儲(chǔ)顯示值的全局變量
      15 extern unsigned char getByte[];          //定義臨時(shí)變量
      16 extern unsigned char flag;             //接收標(biāo)記
      17 extern unsigned char point;            //指針
      18 
      19 //函數(shù)
      20 extern void Display(unsigned char FirstBit,unsigned char Num);//數(shù)碼管顯示函數(shù)
      21 extern void Init_Timer0(void);//定時(shí)器初始化
      22 extern void InitUART(void);
      23 extern void SendStr(unsigned char *s);
      24 extern void SendByte(unsigned char dat);
      25 
      26 //------------------------------------------------
      27 //主函數(shù)
      28 //------------------------------------------------
      29 void main (void)
      30 {
      31     //發(fā)來的FF EE num AA 或 FF DD num AA返回 AA和FF互換位置
      32     unsigned char answer[5];
      33     unsigned char k,data1,data2;
      34     answer[0]=0xAA;
      35     answer[3]=0xFF;
      36     answer[4]='\0';
      37     TempData[2]=DuanMa[0]; //顯示速度等級
      38     TempData[3]=DuanMa[0];     
      39     PWM_ON=0;
      40 
      41     InitUART();
      42     Init_Timer0();    //初始化定時(shí)器0,主要用于數(shù)碼管動(dòng)態(tài)掃描
      43 
      44     while (1)         //主循環(huán)
      45     {
      46         if(flag==1 && point>3 && getByte[point-4]==0xFF)
      47         {
      48             ES = 0;   //關(guān)串口中斷
      49 
      50             answer[1]=0xFF;
      51             data1=getByte[point-3];
      52             data2=getByte[point-2];
      53             if(data1==0xEE){
      54                 if(0<=data2 && data2<=10){
      55                     PWM_ON=data2;
      56                     TempData[2]=DuanMa[PWM_ON/10]; //顯示速度等級
      57                     TempData[3]=DuanMa[PWM_ON%10];     
      58                     answer[1]=0xEE;
      59                     answer[2]=data2+1;
      60                 }
      61             }else if(data1==0xDD){
      62                     answer[1]=0xDD;
      63                     answer[2]=PWM_ON+1;
      64             }
      65             SendStr(answer);        //應(yīng)答
      66 
      67             for(k=0;k<8;k++)        //清空getByte中數(shù)據(jù)
      68                 getByte[k]=0;
      69             point=0;                //point歸零
      70             flag=0;                    //重置flag標(biāo)志
      71             ES=1;                    //打開串口中斷
      72         }
      73     }
      74 }
      75 
      76 //------------------------------------------------
      77 //定時(shí)器中斷子程序
      78 //------------------------------------------------
      79 void Timer0_isr(void) interrupt 1 
      80 {
      81     static unsigned char count;
      82     TH0=(65536-2000)/256;          //重新賦值 2ms
      83     TL0=(65536-2000)%256;
      84     
      85     Display(0,4);                // 調(diào)用數(shù)碼管掃描
      86     
      87     if (count==PWM_ON) 
      88     {
      89         DCOUT = 0;               //如果定時(shí)等于on的時(shí)間,
      90         //說明作用時(shí)間結(jié)束,輸出低電平
      91     }
      92     count++;
      93     if(count == CYCLE)       //反之低電平時(shí)間結(jié)束后返回高電平
      94     {
      95         count=0;
      96         if(PWM_ON!=0)    //如果開啟時(shí)間是0 保持原來狀態(tài)
      97             DCOUT = 1;          
      98     }
      99 }
      復(fù)制代碼

        整個(gè)工程的功能是遠(yuǎn)程安卓設(shè)備連接上該小風(fēng)扇后,通過發(fā)送幀F(xiàn)F EE num AA來無線控制風(fēng)扇轉(zhuǎn)速(其中num值需滿足0≤num≤10,其中FF和AA是幀頭和幀尾用于驗(yàn)證是否為有效幀)。若小風(fēng)扇風(fēng)速調(diào)節(jié)成功則會(huì)返回給遠(yuǎn)程安卓設(shè)備AA EE num+1 FF來表明設(shè)置成功。此外當(dāng)遠(yuǎn)程設(shè)備發(fā)送FF DD num AA時(shí)將會(huì)獲得AA EE num+1 FF,通過這個(gè)命令可以獲取當(dāng)前的轉(zhuǎn)速。

        這里的answer[5]數(shù)組是用來存儲(chǔ)小風(fēng)扇應(yīng)答信息的,data1、data2用來存儲(chǔ)有效幀的中間兩位,PWM_ON是當(dāng)前的轉(zhuǎn)速,CYCLE是一個(gè)周期長度。在主函數(shù)的32~39行分別對answer固定部分進(jìn)行初始化、數(shù)碼管顯示數(shù)據(jù)TempData[]初始化、風(fēng)扇速度PWM_ON初始化。第41、42行主要初始化串口和定時(shí)器,接著進(jìn)入while主循環(huán)。在主循環(huán)中不斷對收集的數(shù)據(jù)幀進(jìn)行判斷是否為有效幀,如果是有效幀則分析是詢問速度命令還是設(shè)置速度命令,并分情況作出響應(yīng)。在主循環(huán)的最后(67~71)是一些收尾工作:緩沖區(qū)getByte清空、緩沖區(qū)指針point清零、接收標(biāo)志flag重置、以及開中斷。

        第76~99行是定時(shí)器中斷子程序,每隔2ms觸發(fā)一次。在其內(nèi)實(shí)現(xiàn)了對數(shù)碼管的高頻動(dòng)態(tài)刷新和PWM。這里PWM是通過一個(gè)中間變量count來控制,從而實(shí)現(xiàn)在一個(gè)CYCLE*2ms的周期內(nèi)前PWM_ON*2ms時(shí)間輸出高電平的效果。

       

      8 客戶端軟件構(gòu)成模塊

      1) 打開Eclipse點(diǎn)擊File菜單欄下的Import按鈕準(zhǔn)備導(dǎo)入second_test工程(如圖8_1所示)。

      圖 8_1 導(dǎo)入工程

      2) 接著在彈出的Select窗口中選擇Android文件夾下的Existing Android Code Into Workspace點(diǎn)擊next(如圖8_2所示)。

                    圖 8_2 選擇導(dǎo)入類型

      3) 接著在彈出的框中點(diǎn)擊右上角的Browse按鈕,找到要導(dǎo)入的second_test所在路徑,并且需要勾選Copy projects into workspace(如圖8_3所示)。

                    圖 8_3 選擇工程

      4) 最終效果如圖8_4所示在src文件夾下有兩個(gè)包:其中上面一個(gè)是和藍(lán)牙相關(guān)的類(從下到上依次為藍(lán)牙設(shè)備搜索相關(guān)類、藍(lán)牙通信連接相關(guān)類和藍(lán)牙通信相關(guān)類),另一個(gè)包是UI相關(guān)類(上一章已經(jīng)講過ui_main.xml負(fù)責(zé)顯示,UI_Main.java負(fù)責(zé)顯示背后的邏輯實(shí)現(xiàn))。如果讀者導(dǎo)入過程中出現(xiàn)錯(cuò)誤,也可以采用上一章的方法新建一個(gè)工程,然后把src下的文件、layout下的文件和AndroidManifest.xml文件做相應(yīng)的新建或修改。

                          圖 8_4 工程文件結(jié)構(gòu)

       

      9 藍(lán)牙通信三劍客詳解

        從圖8_4大家可以看出整個(gè)工程最重要的部分在于bluetooth包下的藍(lán)牙相關(guān)的三個(gè)類,它們封裝并對外提供藍(lán)牙設(shè)備搜索、建立藍(lán)牙連接以及數(shù)據(jù)傳輸?shù)幕舅{(lán)牙功能。這樣在UI_Main.java中只要做簡單的調(diào)用即可實(shí)現(xiàn)比較繁瑣的藍(lán)牙通信功能,下面將針對它們做詳細(xì)的介紹。

      1)BlueToothSearch主要負(fù)責(zé)藍(lán)牙設(shè)備搜索。仔細(xì)的讀者可能會(huì)發(fā)現(xiàn)它與上一章中的Func_BT.java很類似。如下的構(gòu)造函數(shù)除了去掉了表示信號強(qiáng)弱的RSSI向量去掉和在16行實(shí)例化并啟動(dòng)一個(gè)BTStateThread的線程外基本沒變。

      復(fù)制代碼
       1 public BlueToothSearch(Activity activity, Handler mHandler) {
       2     this.mHandler = mHandler;
       3     this.activity = activity;
       4 
       5     mNameVector = new Vector<String>();// 向量
       6     mAddrVector = new Vector<String>();
       7 
       8     IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
       9     activity.registerReceiver(mReceiver, filter);
      10     filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
      11     activity.registerReceiver(mReceiver, filter);
      12     activity.registerReceiver(mReceiver, filter);
      13 
      14     mBtAdapter = BluetoothAdapter.getDefaultAdapter();
      15     
      16     new BTStateThread().start();//藍(lán)牙狀態(tài)監(jiān)聽
      17 }
      復(fù)制代碼

        openBT函數(shù)和上一章的略有不同:上一章中打開藍(lán)牙設(shè)備函數(shù)的目的是確保本地藍(lán)牙設(shè)備打開的情況下進(jìn)行藍(lán)牙搜索,所以上一章中的函數(shù)體內(nèi)還包含了else語句,同時(shí)用onActivityResult進(jìn)行監(jiān)聽用戶是否授權(quán);本章的openBT函數(shù)僅僅是用來在本地藍(lán)牙設(shè)備沒有開啟時(shí)發(fā)送一個(gè)Intent請求,接著就撒手不管了。

      復(fù)制代碼
      1 public void openBT() {
      2     // 如果沒有打開則打開
      3     if (!mBtAdapter.isEnabled()) {
      4         Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
      5         activity.startActivityForResult(intent, ENABLE_BLUETOOTH);
      6     }
      7 }
      復(fù)制代碼

          這里的doDIscovery函數(shù)并未做修改,仍然是取消正在進(jìn)行的搜索過程并啟動(dòng)新的搜索。

      1 public void doDiscovery() {
      2     if (mBtAdapter.isDiscovering()) {
      3         mBtAdapter.cancelDiscovery();
      4     }
      5     mBtAdapter.startDiscovery();
      6 }

          當(dāng)上面啟動(dòng)藍(lán)牙搜索后,在此過程中所搜到的藍(lán)牙設(shè)備將可以在下面的BroadcastReceiver獲得。這里每次發(fā)現(xiàn)一個(gè)藍(lán)牙設(shè)備時(shí)會(huì)獲取該設(shè)備的名稱和地址并放入相應(yīng)的向量中,在最后搜索結(jié)束時(shí)會(huì)通過handler將該消息傳遞給UI_Main.java。

      復(fù)制代碼
       1 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
       2     @Override
       3     public void onReceive(Context context, Intent intent) {
       4         String action = intent.getAction();
       5         if (BluetoothDevice.ACTION_FOUND.equals(action)) {
       6             BluetoothDevice device = intent
       7                     .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
       8             mNameVector.add(device.getName());
       9             mAddrVector.add(device.getAddress());
      10         } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
      11                 .equals(action)) {
      12             // 藍(lán)牙搜索完畢發(fā)送0x01msg
      13             Message msg = new Message();
      14             msg.what = 0x01;
      15             mHandler.sendMessage(msg);
      16         }
      17     }
      18 };
      復(fù)制代碼

        在構(gòu)造函數(shù)的第16行有個(gè)new BTStateThread().start()語句,其主要功能是周期性檢測本地藍(lán)牙設(shè)備狀態(tài)(如下的BTStateThread類)。此外在run函數(shù)內(nèi)還加入了一旦本地藍(lán)牙狀態(tài)改變則發(fā)送0x10Handler消息,用來及時(shí)地通知UI_Main.java當(dāng)前的本地藍(lán)牙設(shè)備的狀態(tài)。

      復(fù)制代碼
       1 class BTStateThread extends Thread {
       2     public void run() {
       3         boolean oldBTState;
       4         while (true) {
       5             try {
       6                 Thread.sleep(1000);
       7                 oldBTState=BTState;
       8                 BTState = mBtAdapter.isEnabled();
       9                 if(oldBTState!=BTState){//一旦藍(lán)牙狀態(tài)改變就發(fā)送消息
      10                     // 藍(lán)牙狀態(tài)改變發(fā)送0x10消息
      11                     Message msg = new Message();
      12                     msg.what = 0x10;
      13                     mHandler.sendMessage(msg);
      14                 }
      15             } catch (InterruptedException e) {}
      16         }
      17     }
      18 }
      復(fù)制代碼

      2)  BlueToothConnect主要負(fù)責(zé)建立本地和遠(yuǎn)程藍(lán)牙的Bluetooth Socket連接。由于我們在BlueToothSearch中已經(jīng)獲得了周邊藍(lán)牙設(shè)備的名稱和地址,所以(代碼中第3行)這里直接調(diào)用getRemoteDevice函數(shù)右地址直接獲得遠(yuǎn)程藍(lán)牙設(shè)備。接著(代碼中第5行)通過調(diào)用代表目標(biāo)遠(yuǎn)程服務(wù)設(shè)備的BluetoothDevice對象的createRfcommSocketToServiceRecord方法創(chuàng)建客戶端Bluetooth Socket。

      復(fù)制代碼
      1 public void setDevice(String Addr){
      2     mBtAdapter = BluetoothAdapter.getDefaultAdapter();
      3     mmDevice = mBtAdapter.getRemoteDevice(Addr);
      4     try {
      5         mmSocket = mmDevice.createRfcommSocketToServiceRecord(MY_UUID);
      6     } catch (IOException e) {
      7     }
      8 }
      復(fù)制代碼

        上面的setDevice函數(shù)僅僅通過傳入的地址獲得了Bluetooth Socket,接下來需要調(diào)用connect來啟動(dòng)連接。(如下面代碼所示)啟動(dòng)連接是放在一個(gè)獨(dú)立的線程里的,一旦連接建立完畢則通過Handler將該消息通知給activity。

      復(fù)制代碼
       1 public void run() {
       2     setName("ConnectThread");
       3     try {
       4         mmSocket.connect();
       5     } catch (IOException e) {
       6         try {
       7             mmSocket.close();
       8         } catch (IOException e2) {
       9             
      10         }
      11         return;
      12     }
      13     //藍(lán)牙連接完畢發(fā)送0x02msg
      14     Message msg=new Message();
      15     msg.what = 0x02;
      16     mHandler.sendMessage(msg);
      17 }
      復(fù)制代碼

        此外要特別說明下cancel()函數(shù),該函數(shù)體內(nèi)執(zhí)行關(guān)閉藍(lán)牙連接的函數(shù)。因?yàn)樵诤芏鄷r(shí)候,比如讀寫文件、網(wǎng)絡(luò)socket等,由于建立連接后沒有關(guān)閉連接會(huì)導(dǎo)致一些意外的錯(cuò)誤。

      1 public void cancel() {
      2     try {
      3         mmSocket.close();
      4     } catch (IOException e) {
      5     }
      6 }

      3)  BlueToothCommunicate主要負(fù)責(zé)數(shù)據(jù)傳輸。上面已經(jīng)解決了連接建立問題,這樣當(dāng)連接一旦建立,客戶端和服務(wù)器設(shè)備上都會(huì)有Bluetooth Socket。自此之后兩者之間沒有太大的區(qū)別,可以使用這兩種設(shè)備上的Bluetooth Socket來發(fā)送和接收消息(這里因?yàn)镠C-05/06已經(jīng)把藍(lán)牙通信協(xié)議固件化了,所以大家可能不能很好的理解上面一段話的精妙之處,如果大家自己嘗試開發(fā)一個(gè)手機(jī)和手機(jī)的藍(lán)牙聊天室或者藍(lán)牙對戰(zhàn)游戲就能明白我的意思了)。下面是其構(gòu)造函數(shù),和BlueToothConnect類似負(fù)責(zé)將Activity的Handler傳入。

      1 public BlueToothCommunicate(Handler mHandler) {
      2     this.mHandler = mHandler;    
      3     state=true;
      4 }

        這里的setSocket主要是根據(jù)BlueToothConnect建立的BluetoothSocket來獲取標(biāo)準(zhǔn)輸入輸出流。這樣當(dāng)本地設(shè)備想向遠(yuǎn)程設(shè)備發(fā)送消息時(shí),只要調(diào)用標(biāo)準(zhǔn)輸出流的write函數(shù)即可實(shí)現(xiàn);當(dāng)本地設(shè)備想讀取遠(yuǎn)程設(shè)備發(fā)送過來的消息時(shí),只要調(diào)用標(biāo)準(zhǔn)輸入流的read函數(shù)即可實(shí)現(xiàn)。

      復(fù)制代碼
       1 public void setSocket(BluetoothSocket socket){
       2     mmSocket = socket;
       3     InputStream tmpIn = null;
       4     OutputStream tmpOut = null;
       5     // 獲取輸入輸出流
       6     try {
       7         tmpIn = socket.getInputStream();
       8         tmpOut = socket.getOutputStream();
       9     } catch (IOException e) {
      10     }
      11     mmInStream = tmpIn;
      12     mmOutStream = tmpOut;
      13 }
      復(fù)制代碼

        和硬件部分藍(lán)牙數(shù)據(jù)傳輸類似:對于本地設(shè)備向遠(yuǎn)程設(shè)備發(fā)消息是本地程序可控的,即本地程序控制發(fā)送消息的時(shí)間點(diǎn),因此這里僅僅把發(fā)送數(shù)據(jù)封裝成一個(gè)write函數(shù),一旦程序需要發(fā)送消息直接調(diào)用即可;但是對于遠(yuǎn)端設(shè)備向本地發(fā)送過來的消息本地是不可控的,即本地程序不清楚該消息會(huì)在什么時(shí)候出現(xiàn),在硬件中我們采用了中斷的方式解決的問題,而在這里我們采用一個(gè)獨(dú)立的輪訓(xùn)線程來處理的,這樣一旦有有效信息傳送過來就能夠做出及時(shí)的響應(yīng)(例如可以在有效信息過來時(shí)采用Handler將該消息傳送給Activity,本代碼中沒有做進(jìn)一步優(yōu)化)。

      復(fù)制代碼
       1 // 利用線程一直收數(shù)據(jù)
       2 public void run() {
       3     byte[] buffer = new byte[1024];
       4     int bytes;
       5     // 循環(huán)一直接收
       6     while (state) {
       7         try {
       8             // bytes是返回讀取的字符數(shù)量,其中數(shù)據(jù)存在buffer中
       9             bytes = mmInStream.read(buffer);
      10             String readMessage = new String(buffer, 0, bytes);
      11             Log.i("beautifulzzzz", "read: " + bytes + "  mes: "
      12                     + readMessage);
      13         } catch (IOException e) {
      14             break;
      15         }
      16     }
      17 }
      18 
      19 // 發(fā)送就直接發(fā)送,沒有用線程
      20 public void write(byte[] buffer) throws IOException {
      21     mmOutStream.write(buffer);
      22 }
      復(fù)制代碼

        同樣的這里也需要一個(gè)用來關(guān)閉BluetoothSocket和標(biāo)準(zhǔn)輸入輸出流的cancel函數(shù)。

      復(fù)制代碼
      1 public void cancel(){
      2     try {
      3         state=false;//讓死循環(huán)停止
      4         mmSocket.close();
      5         mmInStream.close();
      6         mmOutStream.close();
      7     } catch (IOException e) {
      8     }
      9 }
      復(fù)制代碼

       

      10 客戶端軟件整體邏輯梳理

        欲較好地梳理整個(gè)安卓工程,一般都是從Activity的onCreate函數(shù)開始的,此外通過結(jié)合對應(yīng)的XML文件能夠更快地理解。下面便是ui_main.xml所對應(yīng)的UI_Main.java中的onCreate函數(shù):該函數(shù)中最占篇幅的莫過于三個(gè)按鈕監(jiān)聽了。

        如代碼所示第54~86行為對應(yīng)XML中加減按鈕的監(jiān)聽,不難看出在mButton2和mButton3中核心是調(diào)用mBlueToothCommunicate.write(buffer)函數(shù)將數(shù)據(jù)幀buffer發(fā)送給遠(yuǎn)程藍(lán)牙設(shè)備。這里要幫大家回憶一下我們硬件設(shè)計(jì)時(shí)規(guī)定的控制命令幀的格式了:(請轉(zhuǎn)到第七節(jié)最后幾段)遠(yuǎn)程設(shè)備通過發(fā)送幀F(xiàn)F EE num AA來無線控制風(fēng)扇轉(zhuǎn)速(其中num值需滿足0≤num≤10,其中FF和AA是幀頭和幀尾用于驗(yàn)證是否為有效幀)。所以在下面代碼中的7~11行是對控制命令幀的設(shè)置(這里初始化buffer[2]=0x00,即初始速度為0)。因此,大家也不難理解在加減按鈕監(jiān)聽中的對buffer[2]范圍的限制以及buffer[2]++和buffer[2]--的用意了。

      復(fù)制代碼
       1 @Override
       2 protected void onCreate(Bundle savedInstanceState) {
       3     super.onCreate(savedInstanceState);
       4     setContentView(R.layout.ui_main);
       5 
       6     //控制命令幀格式(首尾為校驗(yàn),第二:0xEE為設(shè)置速度,0xDD為獲取速度,第三:速度值)
       7     buffer=new byte[4];
       8     buffer[0]=(byte) 0xFF;
       9     buffer[1]=(byte) 0xEE;
      10     buffer[2]=(byte) 0x00;
      11     buffer[3]=(byte) 0xAA;
      12     
      13     //實(shí)例化藍(lán)牙三劍客(搜索、連接、通信)
      14     //myHandler是用來反饋信息的
      15     mBlueToothSearch=new BlueToothSearch(this, myHandler);
      16     mBlueToothConnect=new BlueToothConnect(myHandler);
      17     mBlueToothCommunicate=new BlueToothCommunicate(myHandler);
      18     
      19     mTextView = (TextView)findViewById(R.id.textView1);
      20     
      21     mButton1 = (Button) findViewById(R.id.button_start);
      22     if(mBlueToothSearch.getBT()==true) mButton1.setText("連接我的小風(fēng)扇");
      23     else mButton1.setText("打開藍(lán)牙設(shè)備");
      24     mButton1.setOnClickListener(new OnClickListener() {
      25         @Override
      26         public void onClick(View v) {
      27             if(mButton1.getText().equals("打開藍(lán)牙設(shè)備")){
      28                 mBlueToothSearch.clearVector();
      29                 mBlueToothSearch.openBT();
      30                 mButton1.setText("連接我的小風(fēng)扇");
      31             }else if(mButton1.getText().equals("連接我的小風(fēng)扇")){
      32                 mBlueToothSearch.clearVector();
      33                 mBlueToothSearch.doDiscovery();
      34                 
      35                 mProgressDialog = ProgressDialog.show(UI_Main.this,"進(jìn)入搜索藍(lán)牙設(shè)備階段...", "稍等一下~", true);    
      36             }else{
      37                 if(mBlueToothConnect!=null){
      38                     mBlueToothConnect.cancel();
      39                     mBlueToothConnect=null;
      40                     mBlueToothConnect=new BlueToothConnect(myHandler);
      41                 }
      42                 if(mBlueToothCommunicate!=null){
      43                     mBlueToothCommunicate.cancel();
      44                     mBlueToothCommunicate=null;
      45                     mBlueToothCommunicate=new BlueToothCommunicate(myHandler);
      46                 }
      47                 mButton1.setText("連接我的小風(fēng)扇");
      48                 mButton2.setEnabled(false);
      49                 mButton3.setEnabled(false);
      50             }
      51         }
      52     });
      53     
      54     mButton2=(Button) findViewById(R.id.button_add);
      55     mButton2.setEnabled(false);
      56     mButton2.setOnClickListener(new OnClickListener() {
      57         @Override
      58         public void onClick(View v) {
      59             if(buffer[2]<(byte) 0x0A){
      60                 buffer[2]++;
      61                 try {
      62                     mBlueToothCommunicate.write(buffer);
      63                     mTextView.setText(new Integer(buffer[2]).toString());
      64                 } catch (IOException e) {
      65                     e.printStackTrace();
      66                 }
      67             }
      68         }
      69     });
      70     
      71     mButton3=(Button) findViewById(R.id.button_cut);
      72     mButton3.setEnabled(false);
      73     mButton3.setOnClickListener(new OnClickListener() {
      74         @Override
      75         public void onClick(View v) {
      76             if(buffer[2]>(byte) 0x00){
      77                 buffer[2]--;
      78                 try {
      79                     mBlueToothCommunicate.write(buffer);
      80                     mTextView.setText(new Integer(buffer[2]).toString());
      81                 } catch (IOException e) {
      82                     e.printStackTrace();
      83                 }
      84             }
      85         }
      86     });
      87 }
      復(fù)制代碼

        其實(shí)有一點(diǎn)大家可能注意到了:加減按鈕初始化時(shí)是被setEnabled(false)的!因?yàn)檎{(diào)用藍(lán)牙的write函數(shù)已經(jīng)是藍(lán)牙搜索、建立連接之后的事情了,而在初始化時(shí)我們是不能輕易開放這兩個(gè)按鈕中的write功能的。所以在此之前我們必須保證連接已經(jīng)建立完畢,這就要引出稍微復(fù)雜的mButton1按鈕監(jiān)聽了。

        注意到上面代碼的第22、23兩行,首先調(diào)用mBlueToothSearch的getBT()行數(shù)判斷用戶當(dāng)前藍(lán)牙設(shè)備是否打開,如果打開則mButton1的功能直接可設(shè)置為“連接我的小風(fēng)扇”,否則mButton1要設(shè)置為“打開藍(lán)牙設(shè)備”。從mButton1的監(jiān)聽中可以看出其主要有三個(gè)功能:①當(dāng)本地藍(lán)牙設(shè)備沒有打開時(shí),負(fù)責(zé)調(diào)用mBlueToothSearch.openBT()函數(shù)打開本地藍(lán)牙設(shè)備,并進(jìn)入連接小風(fēng)扇的功能;②當(dāng)本地藍(lán)牙打開并且還未連接遠(yuǎn)程小風(fēng)扇時(shí),負(fù)責(zé)調(diào)用mBlueToothSearch.doDiscovery()函數(shù)開始搜索周邊藍(lán)牙設(shè)備,并啟動(dòng)一個(gè)ProgressDialog告訴用戶稍等;③當(dāng)連接好了之后需要斷開連接時(shí),負(fù)責(zé)調(diào)用藍(lán)牙建立連接和藍(lán)牙通信相關(guān)函數(shù)取消相關(guān)操作并讓加減按鈕失效。

                        圖 10_1 mButton1功能轉(zhuǎn)換圖

        從圖10_1中可以看出有一個(gè)過程筆者打了個(gè)問號,即從點(diǎn)擊mButton1執(zhí)行連接小風(fēng)扇如何變成可控制階段狀態(tài)的中間過程被我偷偷跳過了。上面第②點(diǎn)中講到當(dāng)本地藍(lán)牙打開并且還未連接遠(yuǎn)程小風(fēng)扇時(shí),點(diǎn)擊按鈕會(huì)執(zhí)行mBlueToothSearch.doDiscovery()函數(shù),然后似乎就沒有狀態(tài)變換了。其實(shí)一切的一切都指向了Activity中的myHandler!

      復(fù)制代碼
       1 // 消息句柄(線程里無法進(jìn)行界面更新,所以要把消息從線程里發(fā)送出來在消息句柄里進(jìn)行處理)
       2 public Handler myHandler = new Handler() {
       3     @Override
       4     public void handleMessage(Message msg) {
       5         switch(msg.what){
       6         case 0x00:
       7             break;//出現(xiàn)異?;?yàn)樗阉鞯皆O(shè)備
       8         case 0x01:
       9             mProgressDialog.setTitle("進(jìn)入嘗試連接藍(lán)牙設(shè)備階段...");
      10             //當(dāng)搜索完畢自動(dòng)查找是否是我們的設(shè)備然后嘗試連接
      11             boolean isFind=false;
      12             for(int i=0;i<mBlueToothSearch.mNameVector.size();i++){
      13                 if(mBlueToothSearch.mNameVector.get(i).equals("HC-06")){
      14                     Log.i("beautifulzzzz",mBlueToothSearch.mNameVector.get(i));
      15                     mBlueToothConnect.setDevice(mBlueToothSearch.mAddrVector.get(i));
      16                     mBlueToothConnect.start();
      17                     isFind=true;
      18                     break;
      19                 }
      20             }
      21             if(isFind!=true)mProgressDialog.dismiss();//等待窗口關(guān)閉
      22             break;//搜索完畢
      23         case 0x02:
      24             mProgressDialog.setTitle("進(jìn)入啟動(dòng)通信階段...");
      25             //將上一步獲得的socket傳給藍(lán)牙通信線程并啟動(dòng)線程監(jiān)聽數(shù)據(jù)
      26             mBlueToothCommunicate.setSocket(mBlueToothConnect.mmSocket);
      27             mBlueToothCommunicate.start();
      28 
      29             mProgressDialog.dismiss();//等待窗口關(guān)閉
      30             mButton1.setText("斷開我的小風(fēng)扇");
      31             mButton2.setEnabled(true);
      32             mButton3.setEnabled(true);
      33             break;//連接完畢
      34         case 0x03:break;
      35         case 0x04:break;
      36         case 0x10:
      37             if(mBlueToothSearch.getBT()==true 
      38                          && mButton1.getText().equals("打開藍(lán)牙設(shè)備")){ 
      39                 mButton1.setText("連接我的小風(fēng)扇");
      40             }else if(mBlueToothSearch.getBT()==false){
      41                 if(mBlueToothConnect!=null){
      42                     mBlueToothConnect.cancel();
      43                     mBlueToothConnect=null;
      44                     mBlueToothConnect=new BlueToothConnect(myHandler);
      45                 }
      46                 if(mBlueToothCommunicate!=null){
      47                     mBlueToothCommunicate.cancel();
      48                     mBlueToothCommunicate=null;
      49                     mBlueToothCommunicate=new BlueToothCommunicate(myHandler);
      50                 }
      51                 mButton1.setText("打開藍(lán)牙設(shè)備");
      52                 mButton2.setEnabled(false);
      53                 mButton3.setEnabled(false);
      54             }
      55             break;//藍(lán)牙狀態(tài)改變
      56         default:break;
      57         }
      58     }
      59 };
      復(fù)制代碼

        這時(shí)大家可能會(huì)恍然大悟(想想上一節(jié)講的藍(lán)牙通信三劍客每個(gè)構(gòu)造函數(shù)中的Handler,以及時(shí)不時(shí)地在它們的成員函數(shù)內(nèi)部出現(xiàn)的發(fā)送Handler消息):原來mBlueToothSearch.doDiscovery()執(zhí)行將會(huì)啟動(dòng)藍(lán)牙搜索,在其搜索過程中搜索的設(shè)備名和設(shè)備地址分別存儲(chǔ)在BlueToothSearch的公有成員變量mNameVector和mAddrVector中,然后在本次搜索結(jié)束后會(huì)向Activity發(fā)送一個(gè)類型為0x01的Handler消息,而該消息會(huì)被Activity中的handleMessage接收到:

                          圖 10_2 Handler消息之0x01

        經(jīng)過上面一個(gè)過程最終位于Activity中的handleMessage接收到0x01消息,請看上面代碼的第8~22行:在case 0x01中遍歷所有找到的藍(lán)牙設(shè)備是否有name為“HC-06”的藍(lán)牙設(shè)備(因?yàn)槲矣玫乃{(lán)牙模塊HC-06出廠默認(rèn)的name就是“HC-06”,此外大家可以參看HC-06的AT指令自行設(shè)置其名字)。當(dāng)找到名為“HC-06”的設(shè)備時(shí)(第15、16兩行)將會(huì)把該設(shè)備的地址傳給mBlueToothConnect來獲得遠(yuǎn)程藍(lán)牙設(shè)備,繼而獲得Bluetooth Socket,然后執(zhí)行獨(dú)立線程進(jìn)行啟動(dòng)連接(大家可以結(jié)合上一節(jié)的BlueToothConnect理解)。當(dāng)然也不排除找不到設(shè)備的情況,第21行如果找不到想要的藍(lán)牙設(shè)備則把mProgressDialog等待窗口關(guān)閉。有一點(diǎn)要和大家說一下:這里是為了演示方便而采用name來確定藍(lán)牙設(shè)備,而name會(huì)出現(xiàn)相同的情況,真正應(yīng)用的時(shí)候一定要注意這一點(diǎn)的!

                           圖 10_3 Handler消息之0x02

        上面講到當(dāng)handleMessage收到0x01消息后,首先找到名為“HC-06”的藍(lán)牙設(shè)備地址,然后執(zhí)行圖10_3所示①的操作獲取BluetoothSocket,接著執(zhí)行②操作啟動(dòng)線程。這樣等到RUN函數(shù)內(nèi)藍(lán)牙通信連接建立完畢后會(huì)向Activity發(fā)送0x02消息,又重新交給Activity來處理。

        請看代碼的第23~33行:在case 0x02中的第26、27兩行,首先調(diào)用mBlueToothCommunicate的setSocket方法來將將上一步獲得的socket傳給藍(lán)牙通信線程并啟動(dòng)線程監(jiān)聽數(shù)據(jù),這樣就能實(shí)施藍(lán)牙無線通信了。所以在接下來的29~32行內(nèi)關(guān)閉了等待窗口并使能加減按鈕,使系統(tǒng)運(yùn)行的狀態(tài)轉(zhuǎn)換到圖10_1中的可控階段。

                          圖 10_4 進(jìn)入可控制狀態(tài)

        至此,大家把圖10_2、10_3、10_4的圖連起來,然后再換掉圖10_1的帶問號的部分就是整個(gè)程序的基本狀態(tài)轉(zhuǎn)換圖。此外,細(xì)心的讀者可能會(huì)發(fā)現(xiàn)在Activity中還有0x10這條消息,其實(shí)該消息的發(fā)送者來自BlueToothSearch中的BTStateThread線程。在上一章中提到該線程起監(jiān)視本地藍(lán)牙設(shè)備狀態(tài)的作用,一旦本地藍(lán)牙設(shè)備的狀態(tài)被改變,則會(huì)發(fā)出0x10的消息。這樣在我們的Activity中一旦發(fā)現(xiàn)有0x10這個(gè)消息則改變相應(yīng)的狀態(tài),來提高程序的可靠性(否則中途關(guān)掉藍(lán)牙可能導(dǎo)致整個(gè)狀態(tài)機(jī)紊亂)。

       

      11 最終成果檢查

      怎么樣,上一章玩硬件沒有盡興的同學(xué)這回有感覺了嗎?這個(gè)看似簡單的小風(fēng)扇是不是還有點(diǎn)含金量?哈哈哈,給自己評價(jià)一下吧:

      • 自己焊制出51最小系統(tǒng)并成功給它燒個(gè)小程序(+ 20分)
      • 明白直流電機(jī)電路設(shè)計(jì)并理解了PWM的51編程(+ 10分)
      • 理解了3461AS的原理,并成功設(shè)計(jì)出自己的數(shù)碼管驅(qū)動(dòng)(+ 20分)
      • 實(shí)現(xiàn)了51串口通信,能對電腦說hello嗎(+ 10分)
      • 大致明白安卓藍(lán)牙相關(guān)API并理解本章介紹的藍(lán)牙三劍客(+ 30分)
      • 腦袋里走通了整個(gè)客戶端軟件的狀態(tài)轉(zhuǎn)換圖(+ 30分)
      • 成功DIY出無線小風(fēng)扇系統(tǒng)(+30)
      • 在無線小風(fēng)扇的基礎(chǔ)上設(shè)計(jì)出無線小臺(tái)燈(+40)
      • 獲得了超過10個(gè)人的贊揚(yáng)(+20)
      • ……

      及格分70分,對自己要狠一點(diǎn)哦,否則后面有你受的!哈哈哈?。?!

      [搜索:beautifulzzzz(看樓主博客園官方博客,享高質(zhì)量生活)嘻嘻?。?!]

      [如果有需要制作藍(lán)牙防丟器或藍(lán)牙室內(nèi)定位的可以聯(lián)系我哦~]

      如果您覺得不錯(cuò),別忘點(diǎn)個(gè)贊讓更多的小伙伴看到\(^o^)/~ 

       

       

       

       

       

        本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多