小项目:蓝牙模块点亮RGB三色灯

在之前的教程中,我们学习了蓝牙模块的原理,并动手写了驱动,实现了串口的接收和发送。本次我们就来教大家如何使用蓝牙串口控制灯。这是一个简单的示例,展示了如何将蓝牙通信与硬件控制相结合,实现远程控制的功能。你也可以扩展这个示例,添加更多的指令和功能,以满足自己的需求。

1. 源码下载及前置阅读

本文首发 良许嵌入式网https://www.lxlinux.net/e/ ,欢迎关注!

本文所涉及的源码及安装包如下(由于平台限制,请点击以下链接阅读原文下载):

https://www.lxlinux.net/e/stm32/bluetooth-rgb-led.html

如果你是嵌入式开发小白,那么建议你先读读下面几篇文章。

前期教程,没看过的小伙伴可以先看下。

2. 项目需求

实现目标是我们有一个三色 LED 灯,手机连上蓝牙后,向蓝牙串口发送关键词 green 则绿灯亮,再次发送 green 则绿灯灭,黄灯和红灯的关键词是 yellow、red ,效果类似。

3. 编程实战

3.1 硬件接线

本教程使用的硬件如下:

  • 单片机:STM32F103C8T6
  • 蓝牙模块:HC-08
  • 小灯:三色 LED 灯模块
  • 串口:USB 转 TTL
  • 烧录器:ST-LINK V2
HC-08LEDSTM32USB 转 TTL
VCC3.3
RXDA2
TXDA3
GNDG
RA5
YA6
GA7
GNDG
A10TX
A9RX
GGND

烧录的时候接线如下表,如果不会烧录的话可以看我之前的文章 STM32下载程序的五种方法:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html。

ST-Link V2STM32
SWCLKSWCLK
SWDIOSWDIO
GNDGND
3.3V3V3

接好如下图:

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-using-rxne-time-out.html】

具体代码如下:

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 img

到这里,我们就完成了 MCU 通过蓝牙将数据透传到手机 APP(蓝牙助手)。

当然,我们也可以通过手机 APP 向蓝牙发送数据,MCU 接收到透传的数据之后通过串口助手打印在电脑上。

比如我们给蓝牙模块发送数据 green 、yellow、red。

可以看到串口助手成功接收到了 green 、yellow、red,这些数据。

我们的三个小灯也打开了。(我的小绿灯不是很亮,用旧了,嘻嘻)

再次发送关键词即可关对应的灯。当然,一次发送 「green yellow red」,就可以控制三个小灯一起反转。

总结

祝贺大家成功点灯!当然,除了控制灯的开关,蓝牙串口还可以应用于更广泛的场景,如个人电子设备、智能家居控制、健康医疗设备等等。随着技术的不断进步,蓝牙技术将持续演进,并在更多领域发挥作用。希望本文能够为你提供了一个初步的了解,并激发你进一步深入研究和应用蓝牙技术的兴趣。感谢各位看官,love and peace!

另外,想进大厂的同学,一定要好好学算法,这是面试必备的。这里准备了一份 BAT 大佬总结的 LeetCode 刷题宝典,很多人靠它们进了大厂。

刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!

有收获?希望老铁们来个三连击,给更多的人看到这篇文章

推荐阅读:

欢迎关注我的博客:良许嵌入式教程网,满满都是干货!

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
您好!要使用HC-05蓝牙模块控制STM32点亮LED灯,您可以按照以下步骤进行操作: 1. 硬件准备: - 将HC-05蓝牙模块连接到STM32开发板。 - 将LED连接到STM32开发板的一个GPIO引脚。 2. 软件设置: - 配置STM32的GPIO引脚,将其设置为输出模式。 - 配置USART串口通信,用于与HC-05模块进行通信。 3. 编写代码: - 在主函数中,初始化USART串口和GPIO引脚。 - 设置一个接收缓冲区,用于接收蓝牙模块发送的数据。 - 在主循环中,检查接收缓冲区是否有数据。 - 如果有数据,检查其内容并执行对应的操作,例如控制LED的点亮和熄灭。 下面是一个简单的示例代码,可以帮助您开始: ```c #include "stm32f4xx.h" // 定义LED引脚 #define LED_PIN GPIO_Pin_13 #define LED_GPIO_PORT GPIOG // 定义USART和接收缓冲区 #define USART USART2 #define USART_BAUDRATE 9600 #define BUFFER_SIZE 10 char rxBuffer[BUFFER_SIZE]; uint8_t rxIndex = 0; void USART2_IRQHandler(void) { if (USART_GetITStatus(USART, USART_IT_RXNE) != RESET) { char rxData = USART_ReceiveData(USART); rxBuffer[rxIndex++] = rxData; if (rxIndex >= BUFFER_SIZE) { rxIndex = 0; } } } void USART_Init(void) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // 配置USART引脚 GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置USART参数 USART_InitStructure.USART_BaudRate = USART_BAUDRATE; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART, &USART_InitStructure); // 配置USART中断 NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_ITConfig(USART, USART_IT_RXNE, ENABLE); USART_Cmd(USART, ENABLE); } void GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); GPIO_InitStructure.GPIO_Pin = LED_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure); } int main(void) { USART_Init(); GPIO_Init(); while (1) { if (rxIndex > 0) { // 处理接收到的蓝牙数据 if (rxBuffer[0] == '1') { GPIO_SetBits(LED_GPIO_PORT, LED_PIN); // 点亮LED } else if (rxBuffer[0] == '0') { GPIO_ResetBits(LED_GPIO_PORT, LED_PIN); // 熄灭LED } rxIndex = 0; } } } ``` 请注意,这只是一个简单的示例代码,仅用于演示基本的通信和控制操作。您可能需要根据具体的硬件和需求进行适当的修改和调整。 希望这能帮到您!如有任何疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

良许Linux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值