【WCH蓝牙系列芯片】-基于CH585开发板—蓝牙主从一体添加串口功能,实现主机从机蓝牙串口透传功能

-------------------------------------------------------------------------------------------------------------------------------------

  在蓝牙开发中,通常将设备分为蓝牙主机(Central)或蓝牙从机(Peripheral)。但在一些复杂的应用场景(中继器),我们需要一个设备既能连接别人的蓝牙设备(做主机),又能被手机连接配置(做从机,这个是主从一体的程序,在CH585EVT例程中有 CentPeri——主从一体例程,整合了主机例程和从机例程的功能同时运行

在这个 CentPeri程序基础上,我们加上双向串口透传功能,

  当CH585作为主机连接从机时:串口收到的数据 -> 发送给从机;从机发来的数据 -> 串口打印。

  当CH585作为从机被手机连接时:串口收到的数据 -> 发送给手机;手机发来的数据 -> 串口打印。

  智能分配:程序自动判断当前的连接状态,将串口数据路由到正确的蓝牙链路。

  一、创建串口处理函数

  为了实现代码的兼容性,利用一个公共的串口处理模块 app_uart_common.c和app_uart_common.h。充当了 UART 硬件蓝牙协议栈 之间的桥梁。

image

/*
 * app_uart_common.c
 *
 */
#include "CONFIG.h"
#include "devinfoservice.h"
#include "gattprofile.h"
#include "peripheral.h"
#include "central.h"    // 为了获取 centralTaskId
#include "ble_uart_service.h"
#include "app_drv_fifo.h"
#include "app_uart_common.h"

/* * 临时缓冲区:用于从软件FIFO中取出一块数据,准备交给蓝牙发送。
 * 大小通常与蓝牙MTU(最大传输单元)相关,这里减去头部开销。
 */
uint8_t to_test_buffer[BLE_BUFF_MAX_LEN - 4 - 3];

//The tx buffer and rx buffer for app_drv_fifo  软件FIFO的底层存储数组。
//length should be a power of 2  必须是2的幂次方(如512, 2048)
static uint8_t app_uart_tx_buffer[APP_UART_TX_BUFFER_LENGTH] = {0};
static uint8_t app_uart_rx_buffer[APP_UART_RX_BUFFER_LENGTH] = {0};
/* FIFO 控制结构体(包含读写指针、长度等信息) */
 app_drv_fifo_t app_uart_tx_fifo;
 app_drv_fifo_t app_uart_rx_fifo;

//interupt uart rx flag ,clear at main loop
 volatile bool uart_rx_flag = false;  //串口接收标志位,当串口中断里收到数据并存入FIFO后,标志置为 true。

//for interrupt rx blcak hole ,when uart rx fifo full
 //当FIFO满时,为了清除串口硬件寄存器的数据防止卡死,将数据读到这里丢弃
 volatile uint8_t for_uart_rx_black_hole = 0;

//fifo length less that MTU-3, retry times  重试计数器:当蓝牙忙碌发送失败时
  uint32_t uart_to_ble_send_evt_cnt = 0;

  /* 引用主机和从机的任务ID,用于通知主机或从机任务“有数据要发” */
  extern uint8_t Peripheral_TaskID;
  extern uint8_t centralTaskId;

void app_uart_init()
{
    //tx fifo and tx fifo  初始化软件FIFO
    //The buffer length should be a power of 2
    app_drv_fifo_init(&app_uart_tx_fifo, app_uart_tx_buffer, APP_UART_TX_BUFFER_LENGTH);
    app_drv_fifo_init(&app_uart_rx_fifo, app_uart_rx_buffer, APP_UART_RX_BUFFER_LENGTH);

    //uart tx io  配置串口引脚 (TX)
    GPIOA_SetBits(bTXD3);
    GPIOA_ModeCfg(bTXD3, GPIO_ModeOut_PP_5mA);
    //uart rx io  配置串口引脚 (RX)
    GPIOA_SetBits(bRXD3);
    GPIOA_ModeCfg(bRXD3, GPIO_ModeIN_PU);
    //uart3 init  初始化 UART3
    UART3_DefInit();

    //enable interupt  开启串口中断
    // RB_IER_RECV_RDY: 接收数据可用中断 (收到数据就触发)
    // RB_IER_LINE_STAT: 线路状态中断 (用于检测错误或溢出)
    UART3_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT);
    PFIC_EnableIRQ(UART3_IRQn);  // 开启中断
}
//蓝牙接收数据回调,将数据写入 TX FIFO(等待主循环通过串口硬件发出去)
void app_uart_tx_data(uint8_t *data, uint16_t length)
{
    uint16_t write_length = length;
    app_drv_fifo_write(&app_uart_tx_fifo, data, &write_length);
}
void app_uart_process(void)
{
    UINT32 irq_status;
    SYS_DisableAllIrq(&irq_status);  // 关中断保护:读取 volatile 标志位前防止中断打断逻辑
    if(uart_rx_flag)   // 如果串口中断里收到了新数据
    {
        uart_rx_flag = false; // 清除标志位
        SYS_RecoverIrq(irq_status); // 尽早恢复中断,减少系统停顿时间

        // 获取当前的连接状态
        bool peri_link = Peripheral_IsConnected(); // 获取从机连接状态
        bool cent_link = Central_IsConnected();    // 获取主机连接状态

        // 只有主机连接 -> 发给主机
        if(cent_link && !peri_link)
        {
             // 发送事件给主机任务(Central Task)  "FIFO里有数据,快去读出来通过蓝牙发走"
            if(centralTaskId != INVALID_TASK_ID) {
                tmos_start_task(centralTaskId, Central_UART_TO_BLE_SEND_EVT, 2);
            }
        }
        // 只有从机连接 -> 发给从机
        else if(!cent_link && peri_link)
        {
            // 发送事件给从机任务(Peripheral Task),来读取数据
            if(Peripheral_TaskID != INVALID_TASK_ID) {
                tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2);
            }
        }
        // 两个都连上了 ->
        else if(cent_link && peri_link)
        {
            // 如果都连上,发给主机,这个是处理发送给主机,也可以发送给从机,这个各自选择
             tmos_start_task(centralTaskId, Central_UART_TO_BLE_SEND_EVT, 2);
        }
          //都没连,清空缓存,防止溢出
          else
          {
              app_drv_fifo_flush(&app_uart_rx_fifo);
          }
    }
    SYS_RecoverIrq(irq_status);  // 如果没有rx_flag,恢复中断

    //tx process  处理蓝牙发来的数据 (BLE -> UART)
    if(R8_UART3_TFC < UART_FIFO_SIZE)  //检查硬件串口的发送FIFO (TFC) 是否还没满。
    {
        // 从软件 TX FIFO 读取数据,直接写入硬件发送寄存器 (R8_UART3_THR)
        // app_drv_fifo_read_to_same_addr 是一个优化函数,
        // 专门用于往同一个硬件地址连续写入多字节数据。
        app_drv_fifo_read_to_same_addr(&app_uart_tx_fifo, (uint8_t *)&R8_UART3_THR, UART_FIFO_SIZE - R8_UART3_TFC);
    }
}
//
//Not every uart reception will end with a UART_II_RECV_TOUT
//UART_II_RECV_TOUT can only be triggered when R8_UARTx_RFC is not 0
//Here we cannot rely UART_II_RECV_TOUT as the end of a uart reception
//串口硬件中断
__INTERRUPT
__HIGH_CODE
void UART3_IRQHandler(void)
{
    uint16_t error;
    switch(UART3_GetITFlag())  // 获取中断标志
    {
        case UART_II_LINE_STAT:
            UART3_GetLinSTA();
            break;

        case UART_II_RECV_RDY:  // 收到数据(FIFO达到触发点)
        case UART_II_RECV_TOUT:  // 接收超时
            error = app_drv_fifo_write_from_same_addr(&app_uart_rx_fifo, (uint8_t *)&R8_UART3_RBR, R8_UART3_RFC);
            if(error != APP_DRV_FIFO_RESULT_SUCCESS)  // 如果软件FIFO写满了
            {
                for(uint8_t i = 0; i < R8_UART3_RFC; i++)  // 必须把硬件FIFO读空,否则中断会一直触发导致死机
                {
                    //fifo full,put to fifo black hole
                    for_uart_rx_black_hole = R8_UART3_RBR;  // 读出来丢进黑洞
                }
            }
            uart_rx_flag = true;  // 置位标志,通知主循环 app_uart_process 有数据来了
            break;

        case UART_II_THR_EMPTY:  // 发送缓冲区空
            break;
        case UART_II_MODEM_CHG:
            break;
        default:
            break;
    }
}

//蓝牙服务事件回调
// connection_handle: 连接句柄,区分是哪个连接
// p_evt: 事件数据结构体
//ble uart service callback handler
void on_bleuartServiceEvt(uint16_t connection_handle, ble_uart_evt_t *p_evt)
{
    switch(p_evt->type)
    {
        case BLE_UART_EVT_TX_NOTI_DISABLED:
            PRINT("%02x:bleuart_EVT_TX_NOTI_DISABLED\r\n", connection_handle);  // 当手机开启了Notify通知
            break;
        case BLE_UART_EVT_TX_NOTI_ENABLED:
            PRINT("%02x:bleuart_EVT_TX_NOTI_ENABLED\r\n", connection_handle);
            break;
        case BLE_UART_EVT_BLE_DATA_RECIEVED:
            PRINT("BLE RX DATA len:%d\r\n", p_evt->data.length); //收到蓝牙数据
#if 0
            //for notify back test
            //to ble
            uint16_t to_write_length = p_evt->data.length;
            app_drv_fifo_write(&app_uart_rx_fifo, (uint8_t *)p_evt->data.p_data, &to_write_length);
            tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2);
            //end of nofify back test
#endif
            //ble to uart
            // 收到蓝牙数据后,直接调用 tx_data 放入 TX FIFO
            // 等待 app_uart_process 在主循环中将其通过串口发给PC
            app_uart_tx_data((uint8_t *)p_evt->data.p_data, p_evt->data.length);

            break;
        default:
            break;
    }
}
/*
 * app_uart_common.h
 */
#ifndef INCLUDE_APP_UART_COMMON_H_
#define INCLUDE_APP_UART_COMMON_H_
#include "CONFIG.h"
#include "devinfoservice.h"
#include "gattprofile.h"
#include "peripheral.h"
#include "ble_uart_service.h"
#include "app_drv_fifo.h"

//The buffer length should be a power of 2
#define APP_UART_TX_BUFFER_LENGTH    512U
#define APP_UART_RX_BUFFER_LENGTH    2048U

extern uint8_t to_test_buffer[BLE_BUFF_MAX_LEN - 4 - 3];
extern app_drv_fifo_t app_uart_tx_fifo;
extern app_drv_fifo_t app_uart_rx_fifo;

//fifo length less that MTU-3, retry times
extern uint32_t uart_to_ble_send_evt_cnt;

// 在 app_uart_common.h 底部添加声明
extern bool Peripheral_IsConnected(void);
extern bool Central_IsConnected(void);

void app_uart_process(void);
void app_uart_init();
void app_uart_tx_data(uint8_t *data, uint16_t length);
void on_bleuartServiceEvt(uint16_t connection_handle, ble_uart_evt_t *p_evt);

#endif /* INCLUDE_APP_UART_COMMON_H_ */

  整个串口与蓝牙之间的收发数据的程序逻辑如下,以下也是CH585_BLE_UART_蓝牙串口透传例程详解的其他博主写的博客,也可仅供参考:

CH585_BLE_UART蓝牙串口透传例程详解ch585蓝牙通信-CSDN博客

  1. 发送路径----PC发数据给蓝牙(UART -> BLE):

    • 电脑的串口调试工具发送 -> CH58x 串口中断 (UART3_IRQHandler) -> 存入 app_uart_rx_fifo -> 置位 uart_rx_flag
    • 主循环 (app_uart_process) 发现标志位 -> 判断蓝牙已连接 -> 触发 TMOS 任务 (Central_UART_TO_BLE_SEND_EVTUART_TO_BLE_SEND_EVT)。
    • TMOS 任务(在 central.cperipheral.c 中)从 app_uart_rx_fifo 取出数据 -> 调用 GATT_WriteNoRspNotify 发送给BLE设备。
  2. 接收路径----蓝牙发数据给PC (BLE -> UART):

    • 蓝牙协议栈收到空中数据 -> 回调 on_bleuartServiceEvt -> 调用 app_uart_tx_data -> 存入 app_uart_tx_fifo
    • 主循环 (app_uart_process) 轮询 -> 发现硬件串口有空闲 (R8_UART3_TFC) -> 从 app_uart_tx_fifo 取出数据写入硬件寄存器 -> 芯片通过 TX 引脚发出。

    在公共处理处理串口程序上,最关键的地方就是智能分发逻辑

image

  这里通过 Peripheral_IsConnectedCentral_IsConnected 实现了动态分配。当HC58x系列芯片做主从一体程序时,只有主机连接时,就启动主机的Central_UART_TO_BLE_SEND_EVT任务事件,只有从机连接时,就启动UART_TO_BLE_SEND_EVT任务事件;但主机和从机同时都连上时,就看各自的选择,这里的程序中,选择启动主机的任务事件。最后一个就是当主机和从机都没有连接时,也可以自行选择,这里直接将数据清空。

二、 主机端逻辑:central.c

  在主机程序中,添加一个Central_UART_TO_BLE_SEND_事件任务,实现状态机来处理蓝牙发送。

  1、串口发送数据到蓝牙主机,主机连接上从机,主机将串口数据在发送到蓝牙从机上
// ================== 新增:处理串口发送事件 (UART -> BLE) ==================
    if(events & Central_UART_TO_BLE_SEND_EVT)
    {
        uint16_t read_length = 0;
        uint8_t result = 0xff;
        // 检查连接状态
        if(centralState != BLE_STATE_CONNECTED)
        {
            // 如果没连接,清空FIFO防止溢出,或者保留数据等连上再发
//                app_drv_fifo_flush(&app_uart_rx_fifo);
            return (events ^ Central_UART_TO_BLE_SEND_EVT);
        }
        // 根据状态机决定是否读取新数据
        switch(send_to_ble_state)
        {
            case SEND_TO_BLE_TO_SEND:
                // 计算当前最大可发送长度 (MTU - 3)
                // 主机端获取MTU通常不需要connHandle参数,或者使用 ATT_GetMTU(centralConnHandle)
                read_length = ATT_GetMTU(centralConnHandle) - 3;

                // 检查FIFO里是否有足够数据
                if(app_drv_fifo_length(&app_uart_rx_fifo) >= read_length)
                {
                    result = app_drv_fifo_read(&app_uart_rx_fifo, to_test_buffer, &read_length);
                    uart_to_ble_send_evt_cnt = 0;
                }
                else
                {
                    // 数据不够MTU长,且等待未超时,先不发,攒一攒
                    if(uart_to_ble_send_evt_cnt < 10)
                    {
                        tmos_start_task(centralTaskId, Central_UART_TO_BLE_SEND_EVT, 4); // 4*0.625ms后再查
                        uart_to_ble_send_evt_cnt++;
                        return (events ^ Central_UART_TO_BLE_SEND_EVT);
                    }
                    // 等太久了,有多少发多少
                    result = app_drv_fifo_read(&app_uart_rx_fifo, to_test_buffer, &read_length);
                    uart_to_ble_send_evt_cnt = 0;
                }
                break;
            case SEND_TO_BLE_ALLOC_FAILED:
            case SEND_TO_BLE_SEND_FAILED:
                // 如果上次失败了,to_test_buffer 里还有数据,不需要重新从FIFO读
                result = APP_DRV_FIFO_RESULT_SUCCESS;
                read_length = 0; // 这里的长度需要依靠上次保留的长度,但在简单逻辑里我们假设数据还在 buffer
                // 注意:严谨做法是需要用一个变量暂存上次的长度,或者这里简化逻辑:
                // 如果使用的是WriteNoRsp,通常失败概率低,这里简化处理,假设 FIFO 读成功
                break;
        }
        // 开始发送 (GATT Write No Response)
        if(result == APP_DRV_FIFO_RESULT_SUCCESS)
        {
            attWriteReq_t req;
            req.cmd = TRUE;  // TRUE = Write No Response (透传推荐,速度快)
            req.sig = FALSE;
            req.handle = centralCharHdl; // ★关键:这是要写入的特征值句柄,必须确保是透传特征值的句柄
            req.len = read_length;       // 刚才读到的长度
            // 分配蓝牙协议栈内存
            req.pValue = GATT_bm_alloc(centralConnHandle, ATT_WRITE_REQ, req.len, NULL, 0);

            if(req.pValue != NULL)
            {
                tmos_memcpy(req.pValue, to_test_buffer, req.len);
                // 调用发送函数
                if(GATT_WriteNoRsp(centralConnHandle, &req) == SUCCESS)
                {
                    send_to_ble_state = SEND_TO_BLE_TO_SEND;
                    // 发送成功,立即触发下一次事件,检查是否还有数据要发
                    tmos_start_task(centralTaskId, Central_UART_TO_BLE_SEND_EVT, 2);
                }
                else
                {
                    // 发送失败 (可能是底层忙),释放内存,下次重试
                    GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
                    send_to_ble_state = SEND_TO_BLE_SEND_FAILED;
                    tmos_start_task(centralTaskId, Central_UART_TO_BLE_SEND_EVT, 5); // 稍后重试
                }
            }
            else
            {
                // 内存分配失败 (协议栈缓存满了)
                send_to_ble_state = SEND_TO_BLE_ALLOC_FAILED;
                tmos_start_task(centralTaskId, Central_UART_TO_BLE_SEND_EVT, 5); // 稍后重试
            }
        }
        return (events ^ Central_UART_TO_BLE_SEND_EVT);
    }

image

image

  在这个任务事件中,最主要的就在获取到主机连接到从机之后,获取handle值,将串口发送的数据to_test_buffer,调用发送函数GATT_WriteNoRsp发送给从机设备。

2、主机连接上从机,主机接收从机发送的NOTIFY数据,在将数据通过蓝牙主机,发送到串口上

  主机接收notify数据通常在 centralProcessGATTMsgATT_HANDLE_VALUE_NOTI

image

    else if(pMsg->method == ATT_HANDLE_VALUE_NOTI)  // 收到从机发来的Notify
    {
        // 直接透传到串口
        app_uart_tx_data(pMsg->msg.handleValueNoti.pValue, pMsg->msg.handleValueNoti.len);
    }

  在主机程序中,最主要的就是添加Central_IsConnected函数,能够知道当前蓝牙主机(Central)是否已经建立连接。

BLE_STATE_CONNECTED: 这是一个枚举值,代表“已连接状态”。作为“状态查询接口”,可以让串口分配问题得以解决

image

bool Central_IsConnected(void)
{
    // 判断状态机是否处于连接状态
    return (centralState == BLE_STATE_CONNECTED);
}

三、 从机端逻辑:peripheral.c

  从机端的逻辑与主机类似,但调用的 API 不同,从机的添加串口,其实和BLE_UART的程序一样的。

  1、串口发送数据到蓝牙从机,主机被主机连接上,从机将串口数据在发送到蓝牙主机notify上

  在 UART_TO_BLE_SEND_EVT 事件中,从机将串口FIFO中的数据读取出来,存放在to_test_buffer中,在利用ble_uart_notify将数据发送到蓝牙主机的Notify上。

  在发送前检查 notify 是否使能是必须的。如果主机没有打开通知(Enable Notify),从机强行发送通常会失败。

image

image

// 将串口收到的数据 -> 发送给蓝牙
     if(events & UART_TO_BLE_SEND_EVT)
     {
         printf("\r\n  [%s]-[%s]-[%u]  \r\n", __FILE__, __FUNCTION__, __LINE__ );
         static uint16_t read_length = 0;
         uint8_t result = 0xff;
         // 状态机:判断当前是“正常发送”还是“发送失败重试”
         switch(send_to_ble_state)
         {
             case SEND_TO_BLE_TO_SEND:  //正常状态,去 FIFO 取数据发送
                 //notify is not enabled  检查连接状态和通知开关
                 // 如果手机没订阅通知(Notify),或者根本没连接,就不能发数据
                 if(!ble_uart_notify_is_ready(peripheralConnList.connHandle))
                 {
                     if(peripheralConnList.connHandle == GAP_CONNHANDLE_INIT)  // 如果是连接断开了
                     {
                         //connection lost, flush rx fifo here
                         app_drv_fifo_flush(&app_uart_rx_fifo);  // 直接清空 RX FIFO,防止连上新手机后收到旧垃圾数据。
                     }
                     break;
                 }
                 read_length = ATT_GetMTU(peripheralConnList.connHandle) - 3; //计算本包最大能发多少字节 (MTU - 3字节协议头)
                 //检查 FIFO 里的数据够不够拼成一个满包
                 if(app_drv_fifo_length(&app_uart_rx_fifo) >= read_length)
                 {
                     PRINT("FIFO_LEN:%d\r\n", app_drv_fifo_length(&app_uart_rx_fifo));  // 够一个包,直接读出来数据长度
                     result = app_drv_fifo_read(&app_uart_rx_fifo, to_test_buffer, &read_length); // 从 FIFO 读数据到 to_test_buffer
                     uart_to_ble_send_evt_cnt = 0; // 清零超时计数器
                 }
                 else // 不够一个包
                 {     // 检查是不是超过10次循环
                     if(uart_to_ble_send_evt_cnt > 10)
                     {
                         result = app_drv_fifo_read(&app_uart_rx_fifo, to_test_buffer, &read_length); // 直接将数据有多少发多少。
                         uart_to_ble_send_evt_cnt = 0;  // 清零计数器
                     }
                     else
                     {
                         // 还没超时,
                         // 重新启动这个任务,延时一会后再来检查
                         tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 4);
                         uart_to_ble_send_evt_cnt++;  // 计数器+1
                         PRINT("NO TIME OUT\r\n");
                     }
                 }
                 // 开始发送数据 从 FIFO读到了数据
                 if(APP_DRV_FIFO_RESULT_SUCCESS == result)
                 {
                     noti.len = read_length; // 设置数据长度
                     // 申请蓝牙协议栈内存
                     noti.pValue = GATT_bm_alloc(peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0);
                     if(noti.pValue != NULL)
                     {
                         tmos_memcpy(noti.pValue, to_test_buffer, noti.len);  // 把数据从临时 buffer 拷贝到蓝牙内存
                         result = ble_uart_notify(peripheralConnList.connHandle, &noti, 0); // 调用底层 ble_uart_notify 发送通知
                         if(result != SUCCESS)// 发送失败
                         {
                             PRINT("R1:%02x\r\n", result);
                             send_to_ble_state = SEND_TO_BLE_SEND_FAILED;  // 切换状态机:下次进来直接重试,不再去 FIFO 取新数据
                             GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI);  // 释放刚才申请的内存
                             tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2);  //重新在来一遍
                         }
                         else// 发送成功
                         {
                             send_to_ble_state = SEND_TO_BLE_TO_SEND;  // 保持正常状态
                             //app_fifo_write(&app_uart_tx_fifo,to_test_buffer,&read_length);
                             //app_drv_fifo_write(&app_uart_tx_fifo,to_test_buffer,&read_length);
                             read_length = 0;
                             tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2);  // 马上下一次任务事件,看看 FIFO 里还有没有剩余数据要发
                         }
                     }
                     else // 内存申请失败 (
                     {
                         send_to_ble_state = SEND_TO_BLE_ALLOC_FAILED;  // 切换状态机:下次重试申请内存
                         tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2); //延时一会执行一遍任务
                     }
                 }
                 else
                 {
                     //send_to_ble_state = SEND_TO_BLE_FIFO_EMPTY;
                 }
                 break;
             case SEND_TO_BLE_ALLOC_FAILED:  // 内存不够
             case SEND_TO_BLE_SEND_FAILED:   // 发送失败

                 noti.len = read_length;
                 noti.pValue = GATT_bm_alloc(peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0);
                 if(noti.pValue != NULL)
                 {
                     tmos_memcpy(noti.pValue, to_test_buffer, noti.len);
                     result = ble_uart_notify(peripheralConnList.connHandle, &noti, 0);
                     if(result != SUCCESS)
                     {
                         PRINT("R2:%02x\r\n", result);
                         send_to_ble_state = SEND_TO_BLE_SEND_FAILED;
                         GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI);
                         tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2);
                     }
                     else
                     {
                         send_to_ble_state = SEND_TO_BLE_TO_SEND;
                         //app_drv_fifo_write(&app_uart_tx_fifo,to_test_buffer,&read_length);
                         read_length = 0;
                         tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2);
                     }
                 }
                 else
                 {
                     send_to_ble_state = SEND_TO_BLE_ALLOC_FAILED;
                     tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2);
                 }
                 break;
             default:
                 break;
         }
         return (events ^ UART_TO_BLE_SEND_EVT);
     }
  2、从机连接上主机,从机接收主机发送的数据,在将数据通过蓝牙从机,发送到串口上

  从机接收数据是通过注册的回调函数实现的。在 on_bleuartServiceEvt 处理,,在蓝牙初始化中,调用ble_uart_add_service(on_bleuartServiceEvt),这个是初始化BLE——UART服务,并且当发生蓝牙事件(如收到数据、通知被打开)时,请执行 on_bleuartServiceEvt 这个函数来处理,然后通过app_uart_tx_data将接收的数据发送出去。

image

//蓝牙服务事件回调
// connection_handle: 连接句柄,区分是哪个连接
// p_evt: 事件数据结构体
//ble uart service callback handler
void on_bleuartServiceEvt(uint16_t connection_handle, ble_uart_evt_t *p_evt)
{
    switch(p_evt->type)
    {
        case BLE_UART_EVT_TX_NOTI_DISABLED:
            PRINT("%02x:bleuart_EVT_TX_NOTI_DISABLED\r\n", connection_handle);  // 当手机开启了Notify通知
            break;
        case BLE_UART_EVT_TX_NOTI_ENABLED:
            PRINT("%02x:bleuart_EVT_TX_NOTI_ENABLED\r\n", connection_handle);
            break;
        case BLE_UART_EVT_BLE_DATA_RECIEVED:
            PRINT("BLE RX DATA len:%d\r\n", p_evt->data.length); //收到蓝牙数据
#if 0
            //for notify back test
            //to ble
            uint16_t to_write_length = p_evt->data.length;
            app_drv_fifo_write(&app_uart_rx_fifo, (uint8_t *)p_evt->data.p_data, &to_write_length);
            tmos_start_task(Peripheral_TaskID, UART_TO_BLE_SEND_EVT, 2);
            //end of nofify back test
#endif
            //ble to uart
            // 收到蓝牙数据后,直接调用 tx_data 放入 TX FIFO
            // 等待 app_uart_process 在主循环中将其通过串口发给PC
            app_uart_tx_data((uint8_t *)p_evt->data.p_data, p_evt->data.length);

            break;
        default:
            break;
    }
}

  在蓝牙从机主要的,也是添加Peripheral_IsConnected函数,用于判断 从机(Peripheral) 角色的连接状态,在从机中用存储 “连接句柄 (Connection Handle)” 的变量来判断,在 BLE 协议中,每当建立一个连接,协议栈就会分配一个唯一的数字(Handle)这个连接,用来区分不同的设备。GAP_CONNHANDLE_INIT通常是0xFFFF是 “无效句柄”“未连接状态”。如果句柄不等于 0xFFFF,说明 “有连接” -> 返回 true。

image

bool Peripheral_IsConnected(void)
{
    // 判断句柄是否有效
    return (peripheralConnList.connHandle != GAP_CONNHANDLE_INIT);
}

四. 移植实现步骤

  如果想移植这串口功能放在自己的程序中,可以根据以下步骤

  1. 添加文件

    • app_uart_common.c 和对应的头文件加入工程。
    • 确保包含 app_drv_fifo.c (FIFO 驱动)。
  2. 修改 main.c

    • main() 函数中调用 app_uart_init()
    • Main_Circulation() (主循环) 中调用 app_uart_process(),实现数据搬运的。
  3. 修改 central.c (主机任务)

    • 注册 Central_ProcessEvent
    • 添加 Central_UART_TO_BLE_SEND_EVT 事件处理分支。
    • centralProcessGATTMsg 中添加 ATT_HANDLE_VALUE_NOTI 处理,调用 app_uart_tx_data
    • 连接成功后: 记得开启 MTU 交换和服务发现,找到对应的特征值句柄 (centralCharHdl)。
    • 添加Central_IsConnected函数
  4. 修改 peripheral.c (从机任务)

    • 调用 ble_uart_add_service(on_bleuartServiceEvt) 注册透传服务。
    • 添加 UART_TO_BLE_SEND_EVT 事件处理分支。
    • 添加Peripheral_IsConnected函数
  5. 配置 TMOS 任务 ID

    • 确保 centralTaskIdPeripheral_TaskIDapp_uart_common.c 中可以访问,让公共串口函数能发送事件。

 

posted on 2026-01-30 10:18  凡仕  阅读(9)  评论(0)    收藏  举报