在之前的教程中,我們學習了藍芽模組的原理,並動手寫了驅動,實現了串列埠的接收和傳送。本次我們就來教大家如何使用藍芽串列埠控制燈。這是一個簡單的示例,展示瞭如何將藍芽通訊與硬體控制相結合,實現遠端控制的功能。你也可以擴充套件這個示例,新增更多的指令和功能,以滿足自己的需求。
1. 原始碼下載及前置閱讀
本文首發 良許嵌入式網 :https://www.lxlinux.net/e/ ,歡迎關注!
本文所涉及的原始碼及安裝包如下(由於平臺限制,請點選以下連結閱讀原文下載):
https://www.lxlinux.net/e/stm32/bluetooth-rgb-led.html
如果你是嵌入式開發小白,那麼建議你先讀讀下面幾篇文章。
- 瞭解不同的下載程式方法,為你的嵌入式開發提供更多選擇:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to...
- 手把手讓你掌握MDK的使用方式和技巧,助你更高效地進行開發:https://www.lxlinux.net/e/stm32/mdk-development-tool-tutorial...
- 逐步引導你入門STM32開發,無需擔心基礎問題:https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginne...
前期教程,沒看過的小夥伴可以先看下。
- 深入瞭解藍芽模組的原理和驅動方法,讓你能夠輕鬆應用於實際專案中:https://www.lxlinux.net/e/stm32/bluetooth-turorial.html
- 嵌入式基本功,為後續學習打下堅實的基礎:https://www.lxlinux.net/e/stm32/stm32-usart-receive-data-usin...
2. 專案需求
實現目標是我們有一個三色 LED 燈,手機連上藍芽後,向藍芽串列埠傳送關鍵詞 green 則綠燈亮,再次傳送 green 則綠燈滅,黃燈和紅燈的關鍵詞是 yellow、red ,效果類似。
3. 程式設計實戰
3.1 硬體接線
本教程使用的硬體如下:
- 微控制器:STM32F103C8T6
- 藍芽模組:HC-08
- 小燈:三色 LED 燈模組
- 串列埠:USB 轉 TTL
- 燒錄器:ST-LINK V2
HC-08 | LED | STM32 | USB 轉 TTL |
---|---|---|---|
VCC | 3.3 | ||
RXD | A2 | ||
TXD | A3 | ||
GND | G | ||
R | A5 | ||
Y | A6 | ||
G | A7 | ||
GND | G | ||
A10 | TX | ||
A9 | RX | ||
G | GND |
燒錄的時候接線如下表,如果不會燒錄的話可以看我之前的文章 STM32下載程式的五種方法:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to...。
ST-Link V2 | STM32 |
---|---|
SWCLK | SWCLK |
SWDIO | SWDIO |
GND | GND |
3.3V | 3V3 |
接好如下圖:
3.2 LED邏輯程式碼
LED 燈的程式碼簡簡單單,只要進行一下三個燈的初始化就行。
void led_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
LED1_GPIO_CLK_ENABLE(); /* LED1時鐘使能 */
LED2_GPIO_CLK_ENABLE(); /* LED2時鐘使能 */
LED3_GPIO_CLK_ENABLE(); /* LED3時鐘使能 */
gpio_init_struct.Pin = LED1_GPIO_PIN; /* LED1引腳 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推輓輸出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct); /* 初始化LED1引腳 */
gpio_init_struct.Pin = LED2_GPIO_PIN; /* LED2引腳 */
HAL_GPIO_Init(LED2_GPIO_PORT, &gpio_init_struct); /* 初始化LED2引腳 */
gpio_init_struct.Pin = LED3_GPIO_PIN; /* LED3引腳 */
HAL_GPIO_Init(LED3_GPIO_PORT, &gpio_init_struct); /* 初始化LED3引腳 */
LED1(0); /* 關閉 LED1 */
LED2(0); /* 關閉 LED2 */
LED3(0); /* 關閉 LED3 */
}
LED 的 .h檔案:
#ifndef _LED_H
#define _LED_H
#include "sys.h"
/******************************************************************************************/
/* 引腳 定義 */
#define LED1_GPIO_PORT GPIOA
#define LED1_GPIO_PIN GPIO_PIN_7
#define LED1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口時鐘使能 */
#define LED2_GPIO_PORT GPIOA
#define LED2_GPIO_PIN GPIO_PIN_6
#define LED2_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口時鐘使能 */
#define LED3_GPIO_PORT GPIOA
#define LED3_GPIO_PIN GPIO_PIN_5
#define LED3_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PB口時鐘使能 */
/******************************************************************************************/
/* LED埠定義 */
#define LED1(x) do{ x ? \
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \
}while(0)
#define LED2(x) do{ x ? \
HAL_GPIO_WritePin(LED2_GPIO_PORT, LED2_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED2_GPIO_PORT, LED2_GPIO_PIN, GPIO_PIN_RESET); \
}while(0)
#define LED3(x) do{ x ? \
HAL_GPIO_WritePin(LED3_GPIO_PORT, LED3_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED3_GPIO_PORT, LED3_GPIO_PIN, GPIO_PIN_RESET); \
}while(0)
/* LED取反定義 */
#define LED1_TOGGLE() do{ HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); }while(0) /* 翻轉LED1 */
#define LED2_TOGGLE() do{ HAL_GPIO_TogglePin(LED2_GPIO_PORT, LED2_GPIO_PIN); }while(0) /* 翻轉LED2 */
#define LED3_TOGGLE() do{ HAL_GPIO_TogglePin(LED3_GPIO_PORT, LED3_GPIO_PIN); }while(0) /* 翻轉LED3 */
/******************************************************************************************/
/* 外部介面函式*/
void led_init(void); /* LED初始化 */
#endif
3.3 藍芽收發
藍芽收發我們在【手把手教你玩轉藍芽模組(原理+驅動):https://www.lxlinux.net/e/stm32/bluetooth-turorial.html】有詳細教程,在這裡就簡單帶過+淺淺複習下,沒看過或者忘記了的小夥伴可以點選連結看看。
藍芽模組透過串列埠與 MCU 進行通訊,所以第一步需要先做好串列埠的配置。
關於串列埠的配置,我寫過一篇文章手把手教你玩串列埠,大家可以移步下文檢視:
【STM32串列埠接收不定長資料(接收中斷+超時判斷):https://www.lxlinux.net/e/stm32/stm32-usart-receive-data-usin...】
具體程式碼如下:
UART_HandleTypeDef bt_uart_handle;
uint8_t bt_uart_rx_buf[BT_RX_BUF_SIZE];
uint8_t bt_uart_tx_buf[BT_TX_BUF_SIZE];
uint16_t bt_uart_rx_len = 0;
void bt_init(uint32_t baudrate)
{
bt_uart_handle.Instance = BT_INTERFACE; /* BT */
bt_uart_handle.Init.BaudRate = baudrate; /* 波特率 */
bt_uart_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 資料位 */
bt_uart_handle.Init.StopBits = UART_STOPBITS_1; /* 停止位 */
bt_uart_handle.Init.Parity = UART_PARITY_NONE; /* 校驗位 */
bt_uart_handle.Init.Mode = UART_MODE_TX_RX; /* 收發模式 */
bt_uart_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 無硬體流控 */
bt_uart_handle.Init.OverSampling = UART_OVERSAMPLING_16; /* 過取樣 */
HAL_UART_Init(&bt_uart_handle); /* 使能BT */
}
void bt_rx_clear(void)
{
memset(bt_uart_rx_buf, 0, sizeof(bt_uart_rx_buf)); //清空接收緩衝區
bt_uart_rx_len = 0; //接收計數器清零
}
void BT_IRQHandler(void)
{
uint8_t receive_data = 0;
if(__HAL_UART_GET_FLAG(&bt_uart_handle, UART_FLAG_RXNE) != RESET){ //獲取接收RXNE標誌位是否被置位
if(bt_uart_rx_len >= sizeof(bt_uart_rx_buf)) //如果接收的字元數大於接收緩衝區大小,
bt_uart_rx_len = 0; //則將接收計數器清零
HAL_UART_Receive(&bt_uart_handle, &receive_data, 1, 1000); //接收一個字元
bt_uart_rx_buf[bt_uart_rx_len++] = receive_data; //將接收到的字元儲存在接收緩衝區
}
if (__HAL_UART_GET_FLAG(&bt_uart_handle, UART_FLAG_IDLE) != RESET) //獲取接收空閒中斷標誌位是否被置位
{
printf("recv: %s\r\n", bt_uart_rx_buf); //將接收到的資料列印出來
control_led(); //檢測是否有LED關鍵詞
bt_rx_clear();
__HAL_UART_CLEAR_IDLEFLAG(&bt_uart_handle); //清除UART匯流排空閒中斷
}
}
透過這幾個函式,我們就可以讀取藍芽返回的資料,並儲存在陣列 bt_uart_rx_buf
裡。
如果需要透過串列埠向藍芽模組傳送資料,可以使用下面函式:
void bt_send(char *fmt, ...)
{
va_list ap;
uint16_t len;
va_start(ap, fmt);
vsprintf((char *)bt_uart_tx_buf, fmt, ap);
va_end(ap);
len = strlen((const char *)bt_uart_tx_buf);
HAL_UART_Transmit(&bt_uart_handle, bt_uart_tx_buf, len, HAL_MAX_DELAY);
}
其實是否向藍芽模組傳送資料並不影響我們的實現效果,留著的目的一方面為了讓大家複習一下,另一方面可以看出藍芽模組是否在正常工作。
至此,藍芽模組的初始化、傳送、接收部分就做好了。
3.4 LED控制
檢測藍芽串列埠是否接收到 LED 關鍵詞,如果有就反轉 LED 燈狀態。
void control_led()
{
if(strstr((const char *)bt_uart_rx_buf, "green") != NULL) //如果接收到關鍵詞"green"
LED1_TOGGLE(); // 翻轉LED1
if(strstr((const char *)bt_uart_rx_buf, "yellow") != NULL) //如果接收到關鍵詞"yellow"
LED2_TOGGLE(); // 翻轉LED2
if(strstr((const char *)bt_uart_rx_buf, "red") != NULL) //如果接收到關鍵詞"red"
LED3_TOGGLE(); // 翻轉LED3
}
3.5 主函式
在 main 函式里,我們可以先呼叫 bt_init()
函式進行初始化,然後呼叫 bt_send()
函式傳送資料。
int main(void)
{
HAL_Init(); /* 初始化HAL庫 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 設定時鐘,72M */
delay_init(72); /* 初始化延時函式 */
usart_init(115200); /* 串列埠1波特率設為115200 */
bt_init(9600); /* 串列埠2波特率設為9600 */
led_init();
printf("藍芽控制燈……\r\n");
while(1)
{
bt_send("bt send\r\n");
delay_ms(1000);
}
}
4. 執行過程
將硬體連好,把串列埠插到電腦 USB 口。
接著我們開啟電腦串列埠軟體。設定串列埠助手波特率 115200(你們不一定要用我這款,隨便的串列埠助手都可以),選擇串列埠號,最後開啟串列埠開始準備接收資料。
這個串列埠工具接收的是 MCU 串列埠 1 的資料,也就是 log 。藍芽接收到資料後,我們使用串列埠 1 列印到下面的串列埠助手裡。
燒錄程式碼,串列埠輸出如下:
然後開啟手機藍芽助手準備開始除錯,(如果有提示下載彈窗的話,點選「下載好了」即可),點選藍芽模組開始連線。沒有藍芽助手的同學,可以在前文找到下載地址。
<img src="https://lxlinux.superbed.verylink.top/item/6577cbd2c458853aef969a92.jpg" alt="img" style="zoom: 50%;" />
<img src="https://lxlinux.superbed.verylink.top/item/6577cc42c458853aef980238.jpg" alt="img" style="zoom: 50%;" />
到這裡,我們就完成了 MCU 透過藍芽將資料透傳到手機 APP(藍芽助手)。
當然,我們也可以透過手機 APP 向藍芽傳送資料,MCU 接收到透傳的資料之後透過串列埠助手列印在電腦上。
比如我們給藍芽模組傳送資料 green 、yellow、red。
可以看到串列埠助手成功接收到了 green 、yellow、red,這些資料。
我們的三個小燈也開啟了。(我的小綠燈不是很亮,用舊了,嘻嘻)
再次傳送關鍵詞即可關對應的燈。當然,一次傳送 「green yellow red」,就可以控制三個小燈一起反轉。
總結
祝賀大家成功點燈!當然,除了控制燈的開關,藍芽串列埠還可以應用於更廣泛的場景,如個人電子裝置、智慧家居控制、健康醫療裝置等等。隨著技術的不斷進步,藍芽技術將持續演進,並在更多領域發揮作用。希望本文能夠為你提供了一個初步的瞭解,並激發你進一步深入研究和應用藍芽技術的興趣。感謝各位看官,love and peace!
另外,想進大廠的同學,一定要好好學演算法,這是面試必備的。這裡準備了一份 BAT 大佬總結的 LeetCode 刷題寶典,很多人靠它們進了大廠。
刷題 | LeetCode演算法刷題神器,看完 BAT 隨你挑!
有收穫?希望老鐵們來個三連擊,給更多的人看到這篇文章
推薦閱讀:
歡迎關注我的部落格:良許嵌入式教程網,滿滿都是乾貨!