免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

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

 

  

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

 

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

  簡(jiǎn)單起見我們實(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號(hào)為51最小系統(tǒng)模塊,起總控作用;2號(hào)為電源模塊,用于向整個(gè)系統(tǒng)供電;3號(hào)為藍(lán)牙模塊,用于單片機(jī)和智能手機(jī)進(jìn)行藍(lán)牙通信;4號(hào)為電機(jī)模塊(包括電機(jī)驅(qū)動(dòng)電路),用于將電能轉(zhuǎn)換為機(jī)械能提供風(fēng);5號(hào)為數(shù)碼管顯示模塊,用于顯示小風(fēng)扇的當(dāng)前轉(zhuǎn)速。

    

              圖 2_1 硬件輪廓勾勒

 

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

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

 

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

  對(duì)于電源模塊,我們可以使用可充電的5V鋰電池或者用3節(jié)1.5V的普通電池湊合。藍(lán)牙模塊是我們上一章中制作藍(lán)牙防丟器的HC-05或HC-06。這里電機(jī)模塊要特別說明下:如圖3_2需要用一個(gè)ULN2003做驅(qū)動(dòng),這樣控制信號(hào)要從4號(hào)引腳輸入以實(shí)現(xiàn)對(duì)馬達(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路位選信號(hào)和8路段選信號(hào)相應(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號(hào)引腳置低電平其余位選引腳置高電平,同時(shí)要讓11、7、5、1、2置高電平其余段選置低電平。

            圖 3_3 3461AS封裝圖

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

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

  

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

  最終我們?cè)O(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ì)

  由上面分析我們知道通過位選信號(hào)和段選信號(hào)的組合可以實(shí)現(xiàn)數(shù)碼管顯示功能。如果采用圖3_6所示電路圖,上面想讓第二個(gè)數(shù)碼管顯示2時(shí),則P0等于0x5b(01011101),P2等于0xdf(11011111)。采用同樣的分析方法我們可以計(jì)算出讓八段數(shù)碼管顯示從0~F的所有P0對(duì)應(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)掃描)這一技巧。如果大家對(duì)動(dòng)態(tài)掃描沒有感覺,可以想象一下?lián)]舞熒光棒時(shí)的樣子——本來只是一根熒光棒,由于揮舞速度比較快而在空中劃出一道美麗的弧線。下面結(jié)合驅(qū)動(dòng)程序和大家詳細(xì)介紹:

 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};//分別對(duì)應(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 }

  這里的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ù)碼管直接對(duì)應(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)功能。

  對(duì)于動(dòng)態(tài)掃描這里用了一個(gè)很巧妙的方法:注意到第15行定義了一個(gè)靜態(tài)變量i,其功能在于實(shí)現(xiàn)一個(gè)周期內(nèi)實(shí)現(xiàn)對(duì)需要點(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”的縮寫,簡(jiǎn)稱脈寬調(diào)制。它是利用微處理器的數(shù)字輸出來對(duì)模擬電路進(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ì)就可以了。

 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)志,通過檢測(cè)是否字符串末尾41     {42         SendByte(*s);43         s++;44     }45 }46 47 //------------------------------------------------48 //串口中斷程序49 //------------------------------------------------50 void UART_SER (void) interrupt 4 //串行中斷服務(wù)程序51 {52     if(RI)                                  //檢測(cè)接收完成標(biāo)志位置153     {54         RI=0;                            //清零接收完成標(biāo)志位55         getByte[point]=SBUF;               //讀取接收到的數(shù)據(jù)56 57         if(getByte[point++]==0xAA)        //遇到可能的結(jié)束標(biāo)志則發(fā)送flag58             flag=1;                        //再主函數(shù)再進(jìn)行判斷是否為有效幀59 60         if(point==8)                    //防止數(shù)組越界61             point=0;62     }63 }

  在該串口驅(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ù)幀,所以包含了對(duì)數(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文件:

 1 #include "../FUNC/display_4X8.h" 2 #include "../FUNC/uart.h" 3 #include "../INTE/inte.h" 4  5 sbit DCOUT = P1^1;//定義電機(jī)信號(hào)輸出端口 6 //------------------------------------------------ 7 //全局變量 8 //------------------------------------------------ 9 unsigned char PWM_ON;   //定義速度等級(jí)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]; //顯示速度等級(jí)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]; //顯示速度等級(jí)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;          //重新賦值 2ms83     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 }

  整個(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è)周期長(zhǎng)度。在主函數(shù)的32~39行分別對(duì)answer固定部分進(jìn)行初始化、數(shù)碼管顯示數(shù)據(jù)TempData[]初始化、風(fēng)扇速度PWM_ON初始化。第41、42行主要初始化串口和定時(shí)器,接著進(jìn)入while主循環(huán)。在主循環(huán)中不斷對(duì)收集的數(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)了對(duì)數(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è)類,它們封裝并對(duì)外提供藍(lán)牙設(shè)備搜索、建立藍(lán)牙連接以及數(shù)據(jù)傳輸?shù)幕舅{(lán)牙功能。這樣在UI_Main.java中只要做簡(jiǎn)單的調(diào)用即可實(shí)現(xiàn)比較繁瑣的藍(lán)牙通信功能,下面將針對(duì)它們做詳細(xì)的介紹。

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

 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 }

  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請(qǐng)求,接著就撒手不管了。

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 }

    這里的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。

 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_FINISHED11                 .equals(action)) {12             // 藍(lán)牙搜索完畢發(fā)送0x01msg13             Message msg = new Message();14             msg.what = 0x01;15             mHandler.sendMessage(msg);16         }17     }18 };

  在構(gòu)造函數(shù)的第16行有個(gè)new BTStateThread().start()語句,其主要功能是周期性檢測(cè)本地藍(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)。

 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 }

2)  BlueToothConnect主要負(fù)責(zé)建立本地和遠(yuǎn)程藍(lán)牙的Bluetooth Socket連接。由于我們?cè)贐lueToothSearch中已經(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對(duì)象的createRfcommSocketToServiceRecord方法創(chuàng)建客戶端Bluetooth Socket。

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 }

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

 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ā)送0x02msg14     Message msg=new Message();15     msg.what = 0x02;16     mHandler.sendMessage(msg);17 }

  此外要特別說明下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)牙對(duì)戰(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)。

 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 }

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

 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 }

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

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 }

 

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

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

  如代碼所示第54~86行為對(duì)應(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ī)定的控制命令幀的格式了:(請(qǐng)轉(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行是對(duì)控制命令幀的設(shè)置(這里初始化buffer[2]=0x00,即初始速度為0)。因此,大家也不難理解在加減按鈕監(jiān)聽中的對(duì)buffer[2]范圍的限制以及buffer[2]++和buffer[2]--的用意了。

 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         @Override26         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         @Override58         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         @Override75         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 }

  其實(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è)問號(hào),即從點(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!

 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 };

  這時(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消息,請(qǐng)看上面代碼的第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來處理。

  請(qǐng)看代碼的第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的帶問號(hào)的部分就是整個(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è)看似簡(jiǎn)單的小風(fēng)扇是不是還有點(diǎn)含金量?哈哈哈,給自己評(píng)價(jià)一下吧:

  • 自己焊制出51最小系統(tǒng)并成功給它燒個(gè)小程序(+ 20分)
  • 明白直流電機(jī)電路設(shè)計(jì)并理解了PWM的51編程(+ 10分)
  • 理解了3461AS的原理,并成功設(shè)計(jì)出自己的數(shù)碼管驅(qū)動(dòng)(+ 20分)
  • 實(shí)現(xiàn)了51串口通信,能對(duì)電腦說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分,對(duì)自己要狠一點(diǎn)哦,否則后面有你受的!哈哈哈?。?!

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

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

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

 

 

 

 

 

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
于RS-485的多機(jī)通信程序(主機(jī)端)
淺析C語言中關(guān)于字符串的操作 - C/C++ - 課堂 - 話題 - 張劍 - CSDN學(xué)...
ATMEGA128相關(guān)例程
libmad音頻解碼庫分析--libmad簡(jiǎn)介
【單片機(jī)筆記】單個(gè)按鍵實(shí)現(xiàn)單擊、雙擊、長(zhǎng)按的實(shí)現(xiàn)
標(biāo)題:LINUX塊設(shè)備分析
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服