淺析STM32F1晶片藍芽HC05模組
1.前期準備
今天,筆者將在STM32F1開發板上,使用一塊HC05模組與手機的BlueToothSerial(即藍芽除錯助手)連線,通過傳送字元給開發板,開發板通過strcmp函式進行對比,無誤則執行對應操作,本實驗將只點亮或者熄滅LED1燈。
準備條件:軟體:MDK5(電腦端)、BlueToothSerial(手機端,可通過華為應用商店進行下載)
硬體:STM32正點原子(F1系列)精英開發板、ST-Link燒錄器、HC05模組一塊、帶藍芽功能耳機一部
硬體連線情況:
其中,需要連線的端子主要是:
KEY HC05晶片的命令資料控制端(推輓輸出),當要傳送命令時要置高,當要傳輸資料時,置低
LED HC05晶片的連線狀態指示端(上拉輸入),當其輸入為1表示已連線,反之未連線
TXD HC05晶片的傳送資料線(PB10 複用推輓輸出)
RXD HC05晶片的接收資料線(PB11 浮空輸入)
VCC 連線3.3V
GND 接地
2.程式設計
本實驗的資料傳輸挺有意思的,通過定時器7定時可以限制接受時間(一到定時時間就通過置高傳輸狀態標誌USART3_RX_STA|=(1<<15)),強制把接收完成,而不是像以前的串列埠實驗那樣,通過回車鍵來判斷本次資料已經結束。
USART3.c
void USART3_IRQHandler(void) //串列埠1中斷服務程式 { u8 Res; if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收中斷 { Res =USART_ReceiveData(USART3); //讀取接收到的資料 if((USART3_RX_STA&(1<<15))==0) //代表上次的已經接收完成,可以開始本次 {if(USART3_RX_STA<USART3_RX_LEN) //當接收的個數少於規定數量USART3_RX_LEN { TIM_SetCounter(TIM7,0); //先把定時器清零 TIM_Cmd(TIM7,ENABLE); //使能定時器,使其開始計數 USART3_RX_BUF[USART3_RX_STA++]=Res; //把資料賦給已經定義好的接收陣列 //這裡為啥不把USART3_RX_STA清零,而是在main函式中呢? //為了可以在mian函式中讀取本次傳輸資料的長度,方便資料的顯示操作及其他操作 }else { USART3_RX_STA|=(1<<15); //接收完成 } } } }
在串列埠3裡面還有一個傳送資料函式比較有趣,他的構造是void u3_printf(char* fmt,...)
對的,你沒看錯,char *fmt,後面接的是“...”三個點,這三個點表示可以是任意型別的資料,他怎麼實現的呢?
舉個例子,說明一下 u3_printf中的引數“...”三個點可以是任意型別的引數
u3_printf("AT+NAME?\r\n"); //傳送AT字串 在此處,這個可變引數是空的,即沒有也可以
u3_printf("ATKHC05 SendText%d\r\n",sendcnt); //在此處,這個可變引數是整數型別,此時va_start(ap,fmt),是把fmt中的可變引數地址賦給ap指標,使其指向那個可變引數
我們通過這個函式程式碼來分析
void u3_Printf(char *fmt,...) //...表示可變引數(多個可變引數組成一個列表,後面有專門的指標指向他),不限定個數和型別, { va_list ap;//初始化指向可變引數列表的指標 char string[256]; va_start(ap,fmt);//將第一個可變引數的地址付給ap,即ap指向可變引數列表的開始 vsprintf(string,fmt,ap);//將引數fmt、ap指向的可變引數一起轉換成格式化字串,放string陣列中,其作用同sprintf(),只是引數型別不同 Uart_SendString(string); //把格式化字串從開發板串列埠送出去 va_end(ap); //ap付值為0,沒什麼實際用處,主要是為程式健壯性
這個可變引數是什麼?怎麼功能這麼強大!!!
依靠的是va_list、va_start、va_end等一組巨集指令進行申明一個可變引數
通過va_list ap;=====>首先在函式裡定義一具VA_LIST型的變數,這個變數是指向引數的指標;
va_start(ap,fmt)=======>然後用VA_START巨集初始化變數剛定義的VA_LIST變數,即把ap指標指向fmt中的可變引數
va_end(ap)========>把ap指標清空
本質上,ap就是一個指向fmt裡面的可變引數的指標,可以是int,float,char等型別
裡面的vsprintf函式的用法同sprintf,都是把格式化字串送到指定陣列中
函式名: vsprintf
功 能: 送格式化輸出串到指定陣列中,第三個引數是可變引數(型別)
用 法: int vsprintf(char *string, char *format, va_list param);
HC05.c
/* 函式名:HC05_Init 功能: 檢測HC05模組是否存在 引數:void 返回:(u8)代表程式執行是否成功標誌 */ u8 HC05_Init(void) { int i,retry=10; u16 temp; u8 res=1; GPIO_InitTypeDef GPIO_InitType; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitType.GPIO_Pin=GPIO_Pin_4; //KEY PA4 推輓輸出 GPIO_InitType.GPIO_Mode=GPIO_Mode_Out_PP;//HC05晶片的命令資料控制端(推輓輸出),當要傳送命令時要置高,當要傳輸資料時,置低 GPIO_InitType.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitType); GPIO_InitType.GPIO_Pin=GPIO_Pin_15; //LED PA15 上拉輸出 GPIO_InitType.GPIO_Mode=GPIO_Mode_IPU;//HC05晶片的連線狀態指示端(上拉輸入),當其輸入為1表示已連線,反之未連線 GPIO_Init(GPIOA,&GPIO_InitType); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); HC05_KEY=1; HC05_LED=1; uart3_init(9600); while(retry--) { HC05_KEY=1; //KEY置高,進入AT模式 delay_ms(10); //只有在AT模式,才可傳送指令 u3_printf("AT\r\n"); //傳送指令 HC05_KEY=0; //退出AT模式,可傳送資料 for(i=0;i<10;i++) //在50ms內等待接收 { delay_ms(5); if(USART3_RX_STA&0x8000) break; } if(USART3_RX_STA&0x8000) //接受到資料的話 { temp=USART3_RX_STA&0x7FFF; //取資料長度 USART3_RX_STA=0; //把接收資料標誌位清零,方便下一次接收的開始 if(temp==4&&(USART3_RX_BUF[0]=='O')&&(USART3_RX_BUF[1]=='K')) { res=0; //當傳送“AT\r\n”指令時,返回的是“OK” break; } } } if(retry==0) res=0xFF; //當迴圈次數達到retry時,返回引數將是報錯標誌0xFF,正常返回的是0 return res; }
其他指令:如查詢主從角色、查詢版本號、查詢藍芽名字、更改波特率等,都大致相同,就不一一累贅。
3.主函式呼叫
實現功能:
1)通過按鍵KEY0按下,開始一直髮送一個數組“ATKHC05SendText%d”(%d是0~99)
2)通過按鍵KEY1按下,傳送指令“AT+ROLE=0”或者“AT+ROLE=1”更改主從角色
3)一直通過if語句對接收標誌進行判斷,時刻準備對手機發送過來的訊號進行處理,
當是"+LED1 ON"指令,strcmp(USART3_RX_BUF,"+LED1 ON"),返回是0,即一樣時進行亮LED1燈操作,
反之,則滅LED1燈。
while(HC05_Init()) //初始化ATK-HC05模組 { LCD_ShowString(30,90,200,16,16,"ATK-HC05 Error!"); delay_ms(500); LCD_ShowString(30,90,200,16,16,"Please Check!!!"); delay_ms(100); } LCD_ShowString(30,90,210,16,16,"KEY1:ROLE KEY0:SEND/STOP"); LCD_ShowString(30,110,200,16,16,"ATK-HC05 Standby!"); LCD_ShowString(30,160,200,16,16,"Send:"); LCD_ShowString(30,180,200,16,16,"Receive:"); POINT_COLOR=BLUE; HC05_Role_Show(); delay_ms(100); USART3_RX_STA=0; while(1) { key=KEY_Scan(0); if(key==KEY1_PRES) //切換模組主從設定 { key=HC05_Get_Role(); if(key!=0XFF) { key=!key; //狀態取反 if(key==0)HC05_Set_Cmd("AT+ROLE=0"); else HC05_Set_Cmd("AT+ROLE=1"); HC05_Role_Show(); HC05_Set_Cmd("AT+RESET"); //復位ATK-HC05模組 delay_ms(200); } }else if(key==KEY0_PRES) { sendmask=!sendmask; //傳送/停止傳送 if(sendmask==0)LCD_Fill(30+40,160,240,160+16,WHITE);//清除顯示 }else delay_ms(10); if(t==50) { if(sendmask) //定時傳送 { sprintf((char*)sendbuf,"ALIENTEK HC05 %d\r\n",sendcnt); LCD_ShowString(30+40,160,200,16,16,sendbuf); //顯示傳送資料 u3_printf("ALIENTEK HC05 %d\r\n",sendcnt); //傳送到藍芽模組 sendcnt++; if(sendcnt>99)sendcnt=0; } HC05_Sta_Show(); t=0; LED0=!LED0; } if(USART3_RX_STA&0X8000) //接收到一次資料了 { LCD_Fill(30,200,240,320,WHITE); //清除顯示 reclen=USART3_RX_STA&0X7FFF; //得到資料長度 USART3_RX_BUF[reclen]=0; //加入結束符 if(reclen==9||reclen==8) //控制DS1檢測 { if(strcmp((const char*)USART3_RX_BUF,"+LED1 ON")==0)LED1=0; //開啟LED1 if(strcmp((const char*)USART3_RX_BUF,"+LED1 OFF")==0)LED1=1;//關閉LED1 } LCD_ShowString(30,200,209,119,16,USART3_RX_BUF);//顯示接收到的資料 USART3_RX_STA=0; } t++; }