实现蓝牙与手机通信

一、蓝牙引脚定义

二、知识点补充

1. 什么是串口通信(UART)

蓝牙模块 ↔ STM32
TXD → RXD (蓝牙发送,STM32接收)
RXD ← TXD (蓝牙接收,STM32发送)

  • TXD:Transmit Data(发送数据)

  • RXD:Receive Data(接收数据)

  • 就像两个人说话:一个说(TX),一个听(RX)

2. 数据格式(9600-8-N-1)

  • 9600:波特率,每秒传输9600个bit

  • 8:每个字节8个数据位

  • N:No Parity,无校验位

  • 1:1个停止位

3.如何接收数据

1.流程图

2. 代码详解 - 接收部分

cpp 复制代码
// bluetooth.c - 接收数据详解

// 全局变量:保存用户注册的回调函数
static Bluetooth_RecvCallback userCallback = NULL;

/**
 * UART5中断服务函数
 * 这是STM32硬件自动调用的,当有数据到达时
 */
void UART5_IRQHandler(void)
{
    uint8_t receivedData;
    
    // 步骤1:检查是否是"接收中断"(数据来了)
    // USART_IT_RXNE = Receive register Not Empty(接收寄存器非空)
    if (USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)
    {
        // 步骤2:清除中断标志(告诉硬件:我收到了,可以准备下一个)
        USART_ClearITPendingBit(UART5, USART_IT_RXNE);
        
        // 步骤3:从UART数据寄存器读取1个字节
        // 这就像从邮箱里拿信
        receivedData = USART_ReceiveData(UART5) & 0xFF;
        // & 0xFF 确保只取低8位
        
        // 步骤4:如果用户注册了回调函数,就调用它
        if (userCallback != NULL)
        {
            // 把收到的数据传给用户的函数处理
            userCallback(receivedData);
        }
    }
}

3. 通俗理解:快递员送包裹

cpp 复制代码
// 想象场景:
// 1. 蓝牙模块 = 快递员
// 2. UART5 = 你家门铃
// 3. 中断函数 = 你开门接快递
// 4. 回调函数 = 你处理快递(拆开看是什么)

void UART5_IRQHandler(void)  // 相当于门铃响了
{
    // 检查是不是快递到了(不是外卖,不是送水的)
    if (USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)
    {
        // 开门签收快递
        USART_ClearITPendingBit(UART5, USART_IT_RXNE);
        
        // 拿到快递包裹
        receivedData = USART_ReceiveData(UART5);
        
        // 看看是谁的快递(回调函数)
        if (userCallback != NULL)
        {
            // 把包裹交给主人处理
            userCallback(receivedData);
        }
    }
}

4.发送数据的过程(轮询方式)

1.流程图

2. 代码详解 - 发送部分

cpp 复制代码
// bluetooth.c - 发送数据详解

/**
 * 发送一个字节
 * 这是"轮询"方式:不断检查状态
 */
void Bluetooth_SendByte(uint8_t data)
{
    // 步骤1:等待发送缓冲区为空
    // 想象:等前一个人写完信,你才能用桌子
    // USART_FLAG_TXE = Transmit register Empty(发送寄存器空)
    while (USART_GetFlagStatus(UART5, USART_FLAG_TXE) == RESET)
    {
        // 如果缓冲区忙,就循环等待
        // 这里可以加超时保护
    }
    
    // 步骤2:把数据放入发送寄存器
    // 就像把信投进邮筒
    USART_SendData(UART5, data);
    
    // 步骤3:等待发送完成
    // 想象:等待邮差把信取走
    // USART_FLAG_TC = Transmission Complete(传输完成)
    while (USART_GetFlagStatus(UART5, USART_FLAG_TC) == RESET)
    {
        // 等待数据完全发出
        // 这里可以加超时保护
    }
    
    // 现在数据已经从TXD引脚发出去了!
}

/**
 * 发送字符串(挨个发送每个字符)
 */
void Bluetooth_SendString(const char *str)
{
    if (str == NULL) return;
    
    // 遍历字符串的每个字符
    while (*str != '\0')  // \0是字符串结束标志
    {
        // 发送当前字符
        Bluetooth_SendByte((uint8_t)(*str));
        // 指针移动到下一个字符
        str++;
    }
}

3. 通俗理解:写信寄信

cpp 复制代码
// 想象场景:
// 1. USART发送寄存器 = 你的写字桌
// 2. 发送数据 = 写信
// 3. TXD引脚 = 邮筒

void Bluetooth_SendByte(uint8_t data)
{
    // 步骤1:看看桌子上有没有东西(别人在用吗?)
    while (USART_GetFlagStatus(UART5, USART_FLAG_TXE) == RESET)
    {
        // 如果桌子上有信,就等着...
        // 相当于:while(桌子不空) { 等待; }
    }
    
    // 步骤2:桌子上空了,开始写信
    USART_SendData(UART5, data);
    // 相当于:把写好的信放在桌子上
    
    // 步骤3:等待邮差来取信
    while (USART_GetFlagStatus(UART5, USART_FLAG_TC) == RESET)
    {
        // 邮差还没来取信,等着...
        // 相当于:while(信还在桌上) { 等待; }
    }
    
    // 步骤4:邮差取走了信(数据发出去了)
}

三、代码实现

1. bluetooth.h

cpp 复制代码
#ifndef __BLUETOOTH_H
#define __BLUETOOTH_H

#include "stm32f10x.h"
#include <stdint.h>

// 蓝牙模块串口配置 - UART5
#define BLUETOOTH_USART        UART5
#define BLUETOOTH_USART_CLK    RCC_APB1Periph_UART5
#define BLUETOOTH_USART_IRQn   UART5_IRQn
#define BLUETOOTH_USART_IRQHandler UART5_IRQHandler

// UART5 GPIO配置
// TX: PC12, RX: PD2
#define BLUETOOTH_TX_GPIO      GPIOC
#define BLUETOOTH_TX_PIN       GPIO_Pin_12
#define BLUETOOTH_TX_GPIO_CLK  RCC_APB2Periph_GPIOC
#define BLUETOOTH_TX_PIN_SOURCE GPIO_PinSource12

#define BLUETOOTH_RX_GPIO      GPIOD
#define BLUETOOTH_RX_PIN       GPIO_Pin_2
#define BLUETOOTH_RX_GPIO_CLK  RCC_APB2Periph_GPIOD
#define BLUETOOTH_RX_PIN_SOURCE GPIO_PinSource2

// 波特率配置
#define BLUETOOTH_BAUDRATE     9600

// 回调函数类型定义
typedef void (*Bluetooth_RecvCallback)(uint8_t data);

// 蓝牙模块初始化状态
typedef enum {
    BLUETOOTH_OK = 0,
    BLUETOOTH_ERROR = 1
} Bluetooth_Status;

// 函数声明
Bluetooth_Status Bluetooth_Init(Bluetooth_RecvCallback recvCallback);
void Bluetooth_SendByte(uint8_t data);
void Bluetooth_SendString(const char *str);
void Bluetooth_DeInit(void);

#endif /* __BLUETOOTH_H */

2. bluetooth.c

cpp 复制代码
#include "bluetooth.h"
#include <string.h>

// 静态变量声明
static Bluetooth_RecvCallback userCallback = NULL;

/**
  * @brief  蓝牙模块初始化
  * @param  recvCallback: 数据接收回调函数指针
  * @retval Bluetooth_Status: 初始化状态
  */
Bluetooth_Status Bluetooth_Init(Bluetooth_RecvCallback recvCallback)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // 保存回调函数
    if (recvCallback == NULL)
    {
        return BLUETOOTH_ERROR;
    }
    userCallback = recvCallback;
    
    // 1. 使能UART5和GPIO时钟
    // UART5时钟在APB1上
    RCC_APB1PeriphClockCmd(BLUETOOTH_USART_CLK, ENABLE);
    
    // GPIO时钟在APB2上
    RCC_APB2PeriphClockCmd(BLUETOOTH_TX_GPIO_CLK | BLUETOOTH_RX_GPIO_CLK, ENABLE);
    
    // 2. 配置UART5 TX引脚(PC12)为复用推挽输出
    GPIO_InitStructure.GPIO_Pin = BLUETOOTH_TX_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BLUETOOTH_TX_GPIO, &GPIO_InitStructure);
    
    // 3. 配置UART5 RX引脚(PD2)为浮空输入
    GPIO_InitStructure.GPIO_Pin = BLUETOOTH_RX_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // 浮空输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BLUETOOTH_RX_GPIO, &GPIO_InitStructure);
    
    // 4. 配置UART5参数
    USART_InitStructure.USART_BaudRate = BLUETOOTH_BAUDRATE;  // 9600波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;  // 8位数据位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;  // 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(BLUETOOTH_USART, &USART_InitStructure);
    
    // 5. 使能UART5接收中断
    USART_ITConfig(BLUETOOTH_USART, USART_IT_RXNE, ENABLE);
    
    // 6. 配置UART5中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = BLUETOOTH_USART_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // 7. 使能UART5
    USART_Cmd(BLUETOOTH_USART, ENABLE);
    
    return BLUETOOTH_OK;
}

/**
  * @brief  发送单个字节数据到蓝牙模块
  * @param  data: 要发送的字节数据
  * @retval 无
  */
void Bluetooth_SendByte(uint8_t data)
{
    // 等待发送缓冲区为空
    while (USART_GetFlagStatus(BLUETOOTH_USART, USART_FLAG_TXE) == RESET)
    {
        // 超时处理(可选)
    }
    
    // 发送数据
    USART_SendData(BLUETOOTH_USART, data);
    
    // 等待发送完成
    while (USART_GetFlagStatus(BLUETOOTH_USART, USART_FLAG_TC) == RESET)
    {
        // 超时处理(可选)
    }
}

/**
  * @brief  发送字符串到蓝牙模块
  * @param  str: 要发送的字符串(以\0结尾)
  * @retval 无
  */
void Bluetooth_SendString(const char *str)
{
    if (str == NULL)
        return;
    
    // 遍历字符串,发送每个字符
    while (*str != '\0')
    {
        Bluetooth_SendByte((uint8_t)(*str));
        str++;
    }
}

/**
  * @brief  UART5中断服务函数
  * @param  无
  * @retval 无
  */
void BLUETOOTH_USART_IRQHandler(void)
{
    uint8_t receivedData;
    
    // 检查是否是接收中断
    if (USART_GetITStatus(BLUETOOTH_USART, USART_IT_RXNE) != RESET)
    {
        // 清除接收中断标志
        USART_ClearITPendingBit(BLUETOOTH_USART, USART_IT_RXNE);
        
        // 读取接收到的数据
        receivedData = USART_ReceiveData(BLUETOOTH_USART) & 0xFF;
        
        // 调用用户注册的回调函数
        if (userCallback != NULL)
        {
            userCallback(receivedData);
        }
    }
}

/**
  * @brief  蓝牙模块反初始化
  * @param  无
  * @retval 无
  */
void Bluetooth_DeInit(void)
{
    // 禁用UART5
    USART_Cmd(BLUETOOTH_USART, DISABLE);
    
    // 禁用UART5中断
    USART_ITConfig(BLUETOOTH_USART, USART_IT_RXNE, DISABLE);
    
    // 清除回调函数
    userCallback = NULL;
}

3.main.c

cpp 复制代码
#include "stm32f10x.h"
#include "bluetooth.h"
#include <string.h>
#include "Delay.h"



/**
  * @brief  蓝牙数据接收回调函数
  * @param  data: 接收到的字节数据
  * @retval 无
  */
void Bluetooth_ReceiveCallback(uint8_t data)
{
    // 功能1: 回显接收到的数据给手机
    Bluetooth_SendByte(data);
    
    // 功能2: 可以根据接收到的数据控制外设(后续扩展)
    // 例如:
    // if (data == '1') {
    //     // 打开LED
    //     GPIO_SetBits(GPIOC, GPIO_Pin_13);
    // } else if (data == '0') {
    //     // 关闭LED
    //     GPIO_ResetBits(GPIOC, GPIO_Pin_13);
    // }
    
    // 功能3: 可以在这里添加数据解析逻辑
    // 例如接收特定指令控制其他外设
}

/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{
    Bluetooth_Status bluetoothStatus;
    
    // 注意:请确保在你的工程中已经初始化了系统时钟
    
    // 初始化蓝牙模块,注册回调函数
    bluetoothStatus = Bluetooth_Init(Bluetooth_ReceiveCallback);
    
    if (bluetoothStatus != BLUETOOTH_OK)
    {
        // 蓝牙初始化失败,可以在这里处理错误
        // 例如闪烁LED提示错误
        while (1);
    }
    
    // 蓝牙初始化成功后,向手机发送提示信息
    Bluetooth_SendString("Bluetooth init success! Ready to recv data...\r\n");
    
    // 主循环
    while (1)
    {
        // 降低CPU占用率,每100ms检查一次
        // 注意:蓝牙数据接收在中断中处理,不在这里
        Delay_ms(100);
        
        // 可以在这里添加其他需要周期性执行的任务
        // 例如:读取传感器数据并通过蓝牙发送
    }
}

// 以下为错误处理函数(标准库需要)
#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
    while (1)
    {
    }
}
#endif

四、 常见问题排查

问题1:收不到数据

  • 检查接线:蓝牙TXD → STM32 PD2(UART5_RX)

  • 检查波特率:双方必须都是9600

  • 检查中断是否使能

问题2:发送数据手机收不到

  • 检查接线:STM32 PC12 → 蓝牙RXD

  • 检查手机蓝牙是否连接

  • 检查手机APP是否正确

问题3:数据乱码

  • 检查串口参数:8-N-1(8位数据,无校验,1停止位)

  • 检查系统时钟:STM32必须是72MHz

五、核心概念总结

中断 vs 轮询

  • 中断:数据来了硬件自动通知你(效率高)

  • 轮询:你不断检查有没有数据(简单但耗CPU)

回调函数

  • 你把处理函数"注册"给我

  • 我收到数据时"回调"你的函数

  • 实现模块解耦:蓝牙模块不关心数据怎么处理

UART通信三要素

  • 波特率一致

  • 数据格式一致

  • 物理连接正确

相关推荐
玉梅小洋4 小时前
手机 App 云端存储云服务选型指南
人工智能·智能手机·手机·工具开发·手机app开发
玉梅小洋6 小时前
手机 App 跨平台框架统一目录构建
智能手机·手机·app开发
东哥笔迹1 天前
高通骁龙Android手机平台EIS基础pipeline(二)
智能手机
jian110581 天前
Android studio 调试flutter 运行自己的苹果手机上
flutter·智能手机·android studio
小锋学长生活大爆炸1 天前
【工具】手机控制iPixel LED屏实现转向和刹车联动、语音控制显示内容
智能手机·工具·led·车机·智能·diy·ipixel
Boxsc_midnight1 天前
【openclaw+imessage】【免费无限流量】集成方案,支持iphone手机+macos
ios·智能手机·iphone
RichardLau_Cx2 天前
【实用工具】2026最新视频压缩工具推荐(PC/在线/手机/命令行全覆盖)
智能手机
感谢地心引力2 天前
安卓、苹果手机无线投屏到Windows
android·windows·ios·智能手机·安卓·苹果·投屏
DS数模2 天前
2026年美赛MCM A题保姆级教程思路分析|A题:智能手机电池消耗建模
数学建模·智能手机·美国大学生数学建模竞赛·美国大学生数学建模·2026美赛·2026美赛a题
TheNextByte12 天前
如何打印Android手机联系人?
android·智能手机