????????嘿,我的小可愛們芋簿!
????????在《藍(lán)牙串口通信》這一章中峡懈,小編帶著大家編寫了藍(lán)牙串口通信程序,測試了藍(lán)牙通信正常与斤。由于我后來又找到了《藍(lán)牙調(diào)試器》軟件肪康,功能強(qiáng)大,可自定義控件撩穿,所以下面將編寫通信協(xié)議磷支,通過這款軟件,來實(shí)現(xiàn)數(shù)據(jù)采集和藍(lán)牙控制食寡。而我們?cè)谏弦徽轮形肀罚瓿闪怂{(lán)牙監(jiān)控界面的設(shè)計(jì),所以這一章中冻河,我們開始講解程序的編寫箍邮。
????????首先,我們要將數(shù)據(jù)包設(shè)置成結(jié)構(gòu)體的形式叨叙,便于后面的操作和管理锭弊,同時(shí)定義接收數(shù)據(jù)堆棧和發(fā)送數(shù)據(jù)堆棧。之所以這樣做擂错,是因?yàn)橥ㄐ艆f(xié)議規(guī)定味滞,數(shù)據(jù)包必須包括起始字節(jié)、數(shù)據(jù)字節(jié)钮呀、校驗(yàn)字節(jié)和結(jié)束字節(jié)剑鞍,這樣做的目的就是確保數(shù)據(jù)傳輸?shù)恼_性和穩(wěn)定性。關(guān)于通信方面的知識(shí)爽醋,我以后用到的話蚁署,還會(huì)介紹的,例如ESP8266等模塊蚂四。
/*定義串口發(fā)送數(shù)據(jù)堆棧*/
typedef struct
{
? ? u8 head;//定義起始字節(jié)
? ? u8 getdata[8];//定義采集數(shù)據(jù),包括溫度(1字節(jié))光戈、濕度(1字節(jié))、是否報(bào)警(1字節(jié))遂赠、電機(jī)運(yùn)行狀態(tài)(1字節(jié))久妆、空氣質(zhì)量(2字節(jié))和測量距離(2字節(jié))
? ? u8 verify;//定義校驗(yàn)字節(jié)
? ? u8 tail;//定義結(jié)束字節(jié)
}TX_STACK;//定義串口發(fā)送數(shù)據(jù)堆棧
/*定義串口接收數(shù)據(jù)堆棧*/
typedef struct
{
? ? u8 head;//定義起始字節(jié)
? ? u8 control[10];//定義閾值存放區(qū),溫濕度上下限(4字節(jié)),空氣質(zhì)量、安全距離(4字節(jié)),取消報(bào)警和啟停電機(jī)(2字節(jié))
? ? u8 verify;//定義校驗(yàn)字節(jié)
? ? u8 tail;//定義結(jié)束字節(jié)
? ? u8 ptr;//定義接收數(shù)據(jù)指針
? ? u8 lock_flag;//定義數(shù)據(jù)接收完畢標(biāo)志位
}RX_STACK;//定義串口接收數(shù)據(jù)堆棧
TX_STACK tx_stack;//定義發(fā)送數(shù)據(jù)緩存區(qū)
RX_STACK rx_stack;//定義接收數(shù)據(jù)緩存區(qū)
關(guān)于藍(lán)牙串口的配置代碼跷睦,在前面已經(jīng)講述過筷弦。下面,我們分為兩個(gè)模塊來講解抑诸。
(1)數(shù)據(jù)采集烂琴,實(shí)時(shí)顯示
這一部分爹殊,是單片機(jī)發(fā)送數(shù)據(jù),通過藍(lán)牙傳輸給手機(jī)奸绷,在手機(jī)界面上顯示單片機(jī)采集到的數(shù)據(jù)边灭。
/*發(fā)送堆棧初始化*/
void tx_init(void)
{
? ? u8 i;
? ? tx_stack.head = 0xa5;//起始字節(jié)
? ? for(i=0; i<8; i++)
? ? {
? ? ? ? tx_stack.getdata[i] = 0x00;//采集數(shù)據(jù)
? ? }
? ? tx_stack.verify = 0x00;
? ? tx_stack.tail = 0x5a;//結(jié)束字節(jié)
}
我們先將發(fā)送堆棧初始化,然后通過下面的發(fā)送程序發(fā)送出去健盒。
/*發(fā)送采集數(shù)據(jù)*/
void send_collected_data(void)
{
? ? u8 i;
? ? //發(fā)送起始字節(jié)
? ? USART_SendData(USART2,tx_stack.head);
? ? while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);
? ? //發(fā)送采集數(shù)據(jù)
? ? for(i=0; i<8; i++)
? ? {
? ? ? ? USART_SendData(USART2, tx_stack.getdata[i]);
? ? ? ? while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);
? ? ? ? tx_stack.verify += tx_stack.getdata[i];
? ? }
? ? //發(fā)送校驗(yàn)字節(jié)
? ? USART_SendData(USART2,tx_stack.verify);
? ? while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);
? ? //發(fā)送結(jié)束字節(jié)
? ? USART_SendData(USART2,tx_stack.tail);
? ? while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);
? ? tx_init();
}
????????在發(fā)送采集數(shù)據(jù)函數(shù)里绒瘦,我們根據(jù)協(xié)議,依次發(fā)送起始字節(jié)扣癣、采集數(shù)據(jù)字節(jié)惰帽、校驗(yàn)字節(jié)和結(jié)束字節(jié)。這樣父虑,在藍(lán)牙軟件里该酗,就會(huì)對(duì)單片機(jī)發(fā)送過來的數(shù)據(jù)進(jìn)行分析處理。首先士嚎,判斷數(shù)據(jù)包是否符合通信協(xié)議呜魄,如果符合(起始字節(jié)、結(jié)束字節(jié)等正確莱衩,數(shù)據(jù)結(jié)構(gòu)正確)爵嗅。再對(duì)接收的數(shù)據(jù)進(jìn)行校驗(yàn),如果校驗(yàn)成功笨蚁,則數(shù)據(jù)有效睹晒,藍(lán)牙軟件就會(huì)顯示對(duì)應(yīng)的數(shù)據(jù),包括文本括细、圖表等形式伪很。如果校驗(yàn)不成功,則數(shù)據(jù)失效奋单,不予處理锉试。(注意:這里只要單片機(jī)依次發(fā)數(shù)據(jù)即可,判斷數(shù)據(jù)包是否符合協(xié)議以及數(shù)據(jù)校驗(yàn)等操作是上位機(jī)軟件程序完成的)
????????temp = distance/100;
? ? ? ? tx_stack.getdata[0] = temperature;
? ? ? ? tx_stack.getdata[1] = humidity;
? ? ? ? tx_stack.getdata[2] = cancel;
? ? ? ? tx_stack.getdata[3] = state;
? ? ? ? tx_stack.getdata[4] = value%256;
? ? ? ? tx_stack.getdata[5] = value/256;
? ? ? ? tx_stack.getdata[6] = temp%256;
? ? ? ? tx_stack.getdata[7] = temp/256;
? ? ? ? send_collected_data();
在主程序里览濒,對(duì)發(fā)送數(shù)據(jù)包的每個(gè)元素采集到的數(shù)值呆盖,再調(diào)用該發(fā)送程序即可。
(2)藍(lán)牙控制匾七,參數(shù)調(diào)整
????????這一部分絮短,是單片機(jī)接收程序江兢,單片機(jī)接收手機(jī)發(fā)出的指令昨忆,并做相關(guān)的處理。
????????我們先要在串口中斷函數(shù)里編寫數(shù)據(jù)包接收語句杉允,相較于發(fā)送(此項(xiàng)目采集數(shù)據(jù)邑贴,實(shí)時(shí)性強(qiáng))席里,接收不是每時(shí)每刻都進(jìn)行的,用中斷的方式可能會(huì)更好些拢驾。在接收中斷函數(shù)里奖磁,我們只要依次接收數(shù)據(jù),將數(shù)據(jù)存放在相應(yīng)的接收數(shù)據(jù)結(jié)構(gòu)體里即可繁疤。
void USART2_IRQHandler(void)
{
? ? static u8 i = 0;
? ? if(USART_GetITStatus(USART2, USART_IT_RXNE)!=RESET)
? ? {
? ? ? ? rev_data = USART_ReceiveData(USART2);? ? ? ? ? ? //接收數(shù)據(jù)
? ? ? ? if(rx_stack.lock_flag == UNLOCK)
? ? ? ? {
? ? ? ? ? ? switch(i)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? case 0:rx_stack.head = rev_data;i++;break;
? ? ? ? ? ? ? ? case 1:rx_stack.control[rx_stack.ptr++] = rev_data;i++;break;
? ? ? ? ? ? ? ? case 2:rx_stack.control[rx_stack.ptr++] = rev_data;i++;break;
? ? ? ? ? ? ? ? case 3:rx_stack.control[rx_stack.ptr++] = rev_data;i++;break;
? ? ? ? ? ? ? ? case 4:rx_stack.control[rx_stack.ptr++] = rev_data;i++;break;
? ? ? ? ? ? ? ? case 5:rx_stack.control[rx_stack.ptr++] = rev_data;i++;break;
? ? ? ? ? ? ? ? case 6:rx_stack.control[rx_stack.ptr++] = rev_data;i++;break;
? ? ? ? ? ? ? ? case 7:rx_stack.control[rx_stack.ptr++] = rev_data;i++;break;
? ? ? ? ? ? ? ? case 8:rx_stack.control[rx_stack.ptr++] = rev_data;i++;break;
? ? ? ? ? ? ? ? case 9:rx_stack.control[rx_stack.ptr++] = rev_data;i++;break;
? ? ? ? ? ? ? ? case 10:rx_stack.control[rx_stack.ptr++] = rev_data;i++;break;
? ? ? ? ? ? ? ? case 11:rx_stack.verify = rev_data;i++;break;
? ? ? ? ? ? ? ? case 12:rx_stack.tail = rev_data;i++;break;
? ? ? ? ? ? ? ? default:rx_stack.lock_flag = LOCKED;i=0;break;
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? USART_ClearITPendingBit(USART2, USART_IT_RXNE);? ? ? //清除接收中斷標(biāo)志
}
????????接收手機(jī)發(fā)送的數(shù)據(jù)包后咖为,我們要對(duì)數(shù)據(jù)包進(jìn)行解析。當(dāng)然在解析之前稠腊,先要判斷數(shù)據(jù)能不能校驗(yàn)成功躁染,如果校驗(yàn)成功,則接收的數(shù)據(jù)正確架忌,做相關(guān)的處理吞彤;否則,接收的數(shù)據(jù)錯(cuò)誤叹放,不做任何處理饰恕。這里,我們需要判斷起始字節(jié)井仰、結(jié)束字節(jié)(固定值0xa5和0x5a)是否符合協(xié)議埋嵌,以及校驗(yàn)字節(jié)是否和數(shù)據(jù)字節(jié)之和低8位是否相等。這里俱恶,由于采用軟件里的自定義控件進(jìn)行數(shù)據(jù)命令的發(fā)送莉恼,所以起始字節(jié)和結(jié)束字節(jié)可不必校驗(yàn)(肯定是固定值0xa5和0x5a)。如果是通過文本框發(fā)送數(shù)據(jù)速那,那么就要校驗(yàn)這兩個(gè)字節(jié)(手動(dòng)輸入俐银,不能確保)。
/*發(fā)送藍(lán)牙控制數(shù)據(jù)*/
void send_control_data(void)
{
? ? u8 sum = 0,i;
? ? if(rx_stack.lock_flag == LOCKED)
? ? {
? ? ? ? for(i=0; i<10; i++)
? ? ? ? {
? ? ? ? ? ? sum += rx_stack.control[i];
? ? ? ? }
? ? ? ? /*檢驗(yàn)成功*/
? ? ? ? if(rx_stack.verify==sum)
? ? ? ? {
? ? ? ? ? ? temper_H = rx_stack.control[0];
? ? ? ? ? ? temper_L = rx_stack.control[1];
? ? ? ? ? ? humid_H = rx_stack.control[2];
? ? ? ? ? ? humid_L = rx_stack.control[3];
? ? ? ? ? ? cancel = rx_stack.control[4];
? ? ? ? ? ? motor_flag = rx_stack.control[5];
? ? ? ? ? ? air_upperlimit = rx_stack.control[6]+rx_stack.control[7]*256;
? ? ? ? ? ? length = rx_stack.control[8]+rx_stack.control[9]*256;
? ? ? ? }
? ? ? ? else
? ? ? ? {
? ? ? ? ? ? ;//校驗(yàn)失敗,不操作
? ? ? ? }
? ? ? ? rx_stack.lock_flag = UNLOCK;
? ? ? ? rx_init();
? ? }
}
當(dāng)然端仰,我們?cè)诖酥靶枰獙?duì)接收數(shù)據(jù)堆棧進(jìn)行初始化捶惜,然后再主函數(shù)中調(diào)用解析函數(shù)即可。
/*接收堆棧初始化*/
void rx_init(void)
{
? ? rx_stack.head = 0x00;//起始字節(jié)
? ? rx_stack.control[0] = temper_H;//數(shù)據(jù)接收控制4字節(jié)
? ? rx_stack.control[1] = temper_L;
? ? rx_stack.control[2] = humid_H;
? ? rx_stack.control[3] = humid_L;
? ? rx_stack.control[4] = cancel;
? ? rx_stack.control[5] = motor_flag;
? ? rx_stack.control[6] = air_upperlimit%256;
? ? rx_stack.control[7] = air_upperlimit/256;
? ? rx_stack.control[8] = length%256;
? ? rx_stack.control[9] = length/256;
? ? rx_stack.verify = 0x00;//數(shù)據(jù)校驗(yàn)字節(jié)
? ? rx_stack.tail = 0x00;//結(jié)束字節(jié)
? ? rx_stack.lock_flag = UNLOCK;
? ? rx_stack.ptr = 0x00;
}
????????到這里荔烧,我們的藍(lán)牙串口通信協(xié)議就編寫完成了吱七,當(dāng)然過程肯定沒有這么簡單,不信你也可以試試鹤竭。此時(shí)踊餐,我們連接好藍(lán)牙,打開之前的多功能時(shí)鐘工程臀稚,就可以在工程界面里看到實(shí)時(shí)采集的數(shù)據(jù)吝岭,并且不斷的更新。同時(shí),你也可以設(shè)置相關(guān)的參數(shù)窜管,UI界面也會(huì)隨著你的操作而會(huì)發(fā)生變化散劫,單片機(jī)也會(huì)做相關(guān)的處理,是不是很神奇幕帆,是不是很有趣获搏,那就一起來開發(fā)多功能時(shí)鐘吧。