????????讀者們,想你們的小編又回來了。
????????從這一章開始,小編將進行上位機與下位機通信的開發(fā),將單片機采集到的數(shù)據(jù)傳輸?shù)绞謾C實時顯示。當(dāng)然,考慮到這部分還是有點難度的,并且,在調(diào)試過程中,我找到兩個藍牙軟件,一個是《藍牙串口》,一個是《藍牙調(diào)試器》。其中《藍牙串口》可以簡單發(fā)送和接收一些字符和16進制數(shù),《藍牙調(diào)試器》的功能比較強大,但在接收發(fā)界面里顯示不出來,不知道是軟件本身問題,還是我沒設(shè)置好相關(guān)參數(shù)。但這款軟件里的專業(yè)調(diào)試界面可以自己布局控件,即不需要編寫代碼就能做出符合自己項目的上位機來,所以還是得佩服一下該開發(fā)者。
????????在本章中,小編將開發(fā)下位機通信程序,結(jié)合《藍牙串口》軟件,實現(xiàn)從手機發(fā)送控制指令,手機再接收單片機發(fā)送過來的相應(yīng)數(shù)據(jù)。在這里,人為規(guī)定,發(fā)送0x01,則手機串口接收到溫度數(shù)據(jù)(1字節(jié)),發(fā)送0x02,則手機串口接收到濕度數(shù)據(jù)(1字節(jié)),發(fā)送0x03,則手機串口接收到空氣質(zhì)量數(shù)據(jù)(若0~255,1字節(jié),若大于255,2字節(jié)),發(fā)送0x04,則手機串口接收到距離數(shù)據(jù)(若0~255,1字節(jié),若大于255,2字節(jié))。
1.資源分析
????????藍牙模塊HC-05,是主從一體的藍牙串口模塊,簡單的說,當(dāng)藍牙設(shè)備與藍牙設(shè)備配對連接成功后,我們可以忽視藍牙內(nèi)部的通信協(xié)議,直接將將藍牙當(dāng)做串口用。當(dāng)建立連接,兩設(shè)備共同使用一通道也就是同一個串口,一個設(shè)備發(fā)送數(shù)據(jù)到通道中,另外一個設(shè)備便可以接收通道中的數(shù)據(jù)。

? ? ? ? 藍牙模塊,共有4個引腳,其中兩個電源引腳VCC和GND,一個數(shù)據(jù)發(fā)送端TX,一個數(shù)據(jù)接收端RX。而stm32f103c8t6具有3個USART串口,USART是一個全雙工通用同步/異步串行收發(fā)模塊。而本次制作,由于USART1串口接口已經(jīng)焊接設(shè)計好,用于與電腦串口通信的(有點尷尬,已損壞,但影響不大),所以采用USART2來通信(TX—>PA2,RX—>PA3)。
2.軟件分析
(1)配置串口相關(guān)的GPIO
void usart_release_gpio_init(void)
{
? ? GPIO_InitTypeDef GPIO_InitStruct;
? ??RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_AFIO, ENABLE);
? ? RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
? ? /*配置PA2為復(fù)用推挽輸出*/
? ? GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
? ? GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
? ? GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
? ? GPIO_Init(GPIOA, &GPIO_InitStruct);
? ? /*配置PA3為復(fù)用浮空輸入*/
? ? GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
? ? GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
? ? GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
? ? GPIO_Init(GPIOA, &GPIO_InitStruct);
}
????????將USART2的TX端口設(shè)置成復(fù)用推挽輸出,RX端口設(shè)置成浮空輸入,在開啟相關(guān)GPIOA時鐘的同時,開啟串口時鐘和復(fù)用時鐘。
(2)?配置相應(yīng)串口模式
void usart_para_config(void)
{
? ? USART_InitTypeDef USART_InitStruct;
? ? USART_InitStruct.USART_BaudRate = 115200;//設(shè)置波特率為115200
? ? USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件流控
? ? USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//發(fā)送和接收模式
? ? USART_InitStruct.USART_Parity = USART_Parity_No;//無奇偶校驗
? ? USART_InitStruct.USART_StopBits = USART_StopBits_1;//停止位1位
? ? USART_InitStruct.USART_WordLength = USART_WordLength_8b;//字長為8位
? ? USART_Init(USART2, &USART_InitStruct);//串口初始化
? ? USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//使能接收中斷
? ? USART_ClearFlag(USART2, USART_FLAG_TC);//清除發(fā)送完成標(biāo)志位
? ? USART_Cmd(USART2, ENABLE);//使能串口2
}
????????串口模式函數(shù)里,由于藍牙AT指令設(shè)置的波特率為115200(不會設(shè)置的可以網(wǎng)上查),設(shè)置成接收模式,通信的數(shù)據(jù)結(jié)構(gòu)為字長為8,無奇偶校驗位,1位停止位。使用USART_Init()函數(shù),將USART2串口初始化。接著,使能接收中斷,清除發(fā)送完成標(biāo)志位,最后使能串口2。
(3)配置NVIC
void usart_nvic_init(void)
{
? ? NVIC_InitTypeDef NVIC_InitStructure;
? ? NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);? ? ? ? ? ? //設(shè)置組優(yōu)先級
? ? NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;? ? ? ? ? //設(shè)置串口2中斷
? ? NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;? //設(shè)置搶占優(yōu)先級1
? ? NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;? ? ? ? //設(shè)置子優(yōu)先級
? ? NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;? ? ? ? ? ? //使能
? ? NVIC_Init(&NVIC_InitStructure);
}
????????由于stm32需要接收手機發(fā)過來的控制指令,而接收通常不是每時每刻的,因此采用中斷的方式去做,所以需要設(shè)置NVIC,以及使能串口中斷。中斷優(yōu)先級的配置已經(jīng)經(jīng)過多次了,如果還是不清楚的話,就上網(wǎng)再查查。
(4)編寫串口接收中斷程序
void USART2_IRQHandler(void)
{
? ? if(USART_GetITStatus(USART2, USART_IT_RXNE)!=RESET)
? ? {
? ? ? ? rev_data = USART_ReceiveData(USART2);? ? ? ? ? ? //接收數(shù)據(jù)
? ? ? ? usart_flag = CHAN_IN;
? ? }
? ? USART_ClearITPendingBit(USART2, USART_IT_RXNE);? ? ? //清除接收中斷標(biāo)志
}
????????串口接收中斷程序里,我們調(diào)用串口接收數(shù)據(jù)函數(shù),暫存在變量rev_data里,然后將接收標(biāo)志位置位,即可在主程序里調(diào)用數(shù)據(jù)發(fā)送語句,否則,主程序是不會發(fā)送數(shù)據(jù)給手機的。
(5)發(fā)送采集數(shù)據(jù)函數(shù)
void bluetooth(void)
{
? ? if(usart_flag == CHAN_IN)
? ? {
? ? ? ? //接收到0x01:發(fā)送溫度,接收到0x02:發(fā)送濕度,
? ? ? ? //接收到0x03:發(fā)送空氣質(zhì)量,接收到0x04:發(fā)送距離
? ? ? ? switch(rev_data)
? ? ? ? {
? ? ? ? ? ? case 0x01:send_data = temperature;break;
? ? ? ? ? ? case 0x02:send_data = humidity;break;
? ? ? ? ? ? case 0x03:send_data = value;break;
? ? ? ? ? ? case 0x04:send_data = distance/100;break;
? ? ? ? ? ? default:break;
? ? ? ? }
? ? ? ? if(rev_data>=0x01&&rev_data<=0x04)
? ? ? ? {
? ? ? ? ? ? send_data = send_data/100*256+((send_data%100)/10)*16+send_data%10;
? ? ? ? ? ? if(send_data<=255&&send_data>0)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? USART_SendData(USART2, send_data);
? ? ? ? ? ? ? ? while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);
? ? ? ? ? ? }
? ? ? ? ? ? else
? ? ? ? ? ? {
? ? ? ? ? ? ? ? USART_SendData(USART2, send_data/256);
? ? ? ? ? ? ? ? while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);
? ? ? ? ? ? ? ? USART_SendData(USART2, send_data%256);
? ? ? ? ? ? ? ? while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? usart_flag = NO_CHAN;
? ? }
}
????????該函數(shù)在主程序中調(diào)用,當(dāng)接收標(biāo)志位被置位,則說明單片機接收到手機發(fā)過來的指令數(shù)據(jù),根據(jù)所發(fā)的不同指令,則單片機發(fā)送不同的采集數(shù)據(jù)給手機,手機接收到,則在手機界面顯示出數(shù)據(jù)。但別忘了,調(diào)用完該發(fā)送函數(shù),需要將接收標(biāo)志位復(fù)位,否則單片機將不斷地發(fā)送數(shù)據(jù)給手機,顯得有些無意義。這里,說明一下,我們有兩個宏定義#define NO_CHAN 0x00 #define CHAN_IN 0x01,這樣做,可能意義會更明顯。在stm32里,小編感覺像宏定義,結(jié)構(gòu)體等用得比較多(可以看一下固件庫函數(shù),都是這樣的),閱讀程序起來也好理解。


????????通過上面的操作,我們將藍牙模塊接好,注意這里藍牙的TX,RX分別接USART2的RX,TX,然后下載好程序,打開《藍牙串口》軟件,點擊搜索,即可搜索到藍牙。在對話框界面,發(fā)送和接收對話框都勾選16進制,在發(fā)送對話框發(fā)送0x01,即可在接收對話框接收到數(shù)據(jù),此時顯示的是溫度的數(shù)據(jù),為12度。此時再依次輸入0x02、0x03、0x04,即可顯示所有數(shù)據(jù),此時溫度為12度,濕度為69%,空氣質(zhì)量為104PPM,距離為26cm。界面里如果不勾選16進制,則顯示亂碼,因為此時顯示的是字符數(shù)據(jù)。你可以在程序中發(fā)送英文字符串,即可將字符串打印出來。但這個軟件不支持中文顯示,所以不能打印中文。
????????在這里,我聲明一下,本章的內(nèi)容,是為了測試stm32串口配置的代碼以及藍牙模塊是否正常工作(由于stm32的usart1硬件電路已損壞,不能連接電腦來調(diào)試串口通信)。在后面,我會介紹如何通過《藍牙調(diào)試器》達到數(shù)據(jù)的實時顯示和監(jiān)控。