由于STM32是没有蓝牙功能的,而我们需要和手机进行通讯,这里我们使用了蓝牙透传模组,通过串口与STM32进行数据透传。

蓝牙部分我们使用串口2进行数据交互,所以需要重写串口2的处理函数

我们使用的蓝牙模组是RF Crazy/智汉的模组,主要是立创上购买方便,大家也可以根据自己需要选择其它模组。

由于模组启动时,模组的名称是RF-CRAZY, 模组的状态会打印到串口上,所以我们需要使用模组提供的AT指令AT+NAME=Mini-Printer\r\n,设置模组的名称为Mini-Printer,这样我们在手机APP中才能找到这个设备。

另外模组工作时,会打印下方这些设备状态,它会影响我们接收打印数据,所以我们需要使用AT指令AT+STATUS=0\r\n关闭掉。

而在开发过程中,和厂家技术沟通时发现,使用AT+STATUS=0\r\n是无法关闭CONNECTED和DISCONNECTED状态的,所以需要我们在代码中自己处理。

这个蓝牙的通讯和初始化写的很好,用的状态机会处理每一步,第一步:蓝牙初始化发送+++,进入AT模式,第二步:设置状态为0,关闭状态显示,第三步:查询状态是否为0,第四步:查询设备名称,第五步:设置设备名称,第六步:退出AT模式,进入数据透传。

数据类型:

typedef enum{ BLE_INIT_START = 0, // 初始化开始 BLE_IN_AT_MODE, // 进入AT模式 BLE_IN_AT_MODE_SUCCESS, // 进入AT模式成功 BLE_CLOSE_STATUS, // 关闭状态显示 BLE_CLOSE_STATUS_SUCCESS, // 关闭状态显示成功 BLE_QUERY_STATUS, // 查询状态 BLE_QUERY_STATUS0_SUCCESS, // 查询状态为0成功 BLE_QUERY_NAME, // 查询设备名称 BLE_NEED_SET_NAME, // 需要设置名称 BLE_NONEED_SET_NAME, // 不需要设置名称 BLE_SET_NAME, // 设置名称 BLE_SET_NAME_SUCCESS, // 设置名称成功 BLE_OUT_AT_MODE, // 退出AT模式 BLE_INIT_FINISH, // 初始化完成 BLE_RESET, // 重置 } e_ble_init_step;

  1. 串口数据流解析与协议识别 (Protocol Parsing & Recognition):
    • 核心机制:uart_cmd_handle() 函数作为串口中断或接收任务的处理核心,维护一个 cmd_buffer 环形缓冲区(实际为线性累加),根据数据特征动态解析。
    • 数据类型:

int cmd_index = 0; uint8_t cmd_buffer[100]; bool need_clean_ble_status = false;

    • 原理:
      • 文本状态过滤:在初始化完成后,若接收到 "CONNECTED"、"DISCONNECTED" 等文本字符串,通过 strstr 识别并标记 need_clean_ble_status,在后续任务中清空缓冲区,防止这些非业务数据干扰打印。
      • 二进制指令识别:
        • 热敏密度控制:检测连续的 0xA5 头部(4字节)+ 1字节参数,根据参数(1, 2, 其他)设置不同的加热密度(30, 60, 100)。
        • 读取完成信号:检测连续的 0xA6 头部,置位 read_ble_finish 标志,通知主程序数据接收完毕。
        • 打印数据透传:当缓冲区累积达到特定长度(48字节)时,判定为打印图片数据,调用 write_to_printbuffer 将其存入打印队列,并累加 packcount 用于流量统计。
  1. 设备状态主动上报 (Active Status Reporting):
    • 核心机制:ble_report() 函数封装了设备状态的结构体数据。
    • 原理:将电池电量、温度、缺纸状态、打印状态打包成 4 字节的紧凑数组,通过 HAL_UART_Transmit 发送给蓝牙模组。这通常用于手机端 App 轮询或模组主动上报,让用户实时了解打印机硬件状态。
  1. AT指令状态机配置 (Finite State Machine Configuration):
    • 核心机制:init_ble() 函数内部维护一个 while(1) 循环,配合 g_ble_init_step 枚举变量构建状态机。
    • 原理:
      • 流程控制:系统启动时,状态机按顺序执行:进入AT模式 (+++) -> 关闭状态显示 (AT+STATUS=0) -> 查询并校验名称 -> 设置名称 -> 退出AT模式。
      • 容错与重试:如果在某一步骤未收到预期的 "OK\r\n" 回复,状态机会停留在当前状态或重置,配合 vTaskDelay 实现延时重试,确保配置的可靠性。
      • 一致性检查:通过查询当前名称并与目标名称比对,仅在名称不一致时才执行写入操作,既保证了配置正确,又减少了不必要的 Flash 擦写(延长模组寿命)。

代码:

void uart_cmd_handle(uint8_t data)
{
    cmd_buffer[cmd_index++] = data;
    
    if(g_ble_init_step == BLE_INIT_FINISH){
        // 连接状态处理
        if(strstr((char*)cmd_buffer, "CONNECTED")){
            need_clean_ble_status = true;
            run_led(LED_CONNECT);
        }
        
        if(strstr((char*)cmd_buffer, "DISCONNECTED")){
            need_clean_ble_status = true;
            run_led(LED_DISCONNECT);
        }
        
        // 错误处理
        if(strstr((char*)cmd_buffer, "DEVICE ERROR")){
            need_clean_ble_status = true;
            run_led(LED_ERROR);
        }
        
        // 命令解析
        if(cmd_index >= 5){
            // 热密度设置命令
            if(memcmp(cmd_buffer, "\xA5\xA5\xA5\xA5", 4) == 0){
                switch(cmd_buffer[4]){
                    case 1: set_heat_density(30); break;
                    case 2: set_heat_density(60); break;
                    default: set_heat_density(100);
                }
                clear_cmd_buffer();
                return;
            }
            
            // 读取完成命令
            if(memcmp(cmd_buffer, "\xA6\xA6\xA6\xA6", 4) == 0){
                set_read_ble_finish(true);
                printf("数据包计数: %d\n", packcount);
                clear_cmd_buffer();
                return;
            }
        }
        
        // 动态命令结束检测
        if(cmd_index >= 5 && cmd_buffer[cmd_index-1] == 0x01){
            if(memcmp(&cmd_buffer[cmd_index-5], "\xA6\xA6\xA6\xA6", 4) == 0){
                printf("数据包计数: %d\n", packcount);
                clear_cmd_buffer();
                set_read_ble_finish(true);
                return;
            }
        }
        
        // 数据包处理
        if(cmd_index >= 48){
            packcount++;
            // 添加数据处理逻辑
        }
    }
}

// 辅助函数
void clear_cmd_buffer()
{
    cmd_index = 0;
    memset(cmd_buffer, 0, sizeof(cmd_buffer));
}
 

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐