【WCH蓝牙系列芯片】-基于CH585开发板—蓝牙主从一体添加串口功能,实现主机从机蓝牙串口透传功能
-------------------------------------------------------------------------------------------------------------------------------------
在蓝牙开发中,通常将设备分为蓝牙主机(Central)或蓝牙从机(Peripheral)。但在一些复杂的应用场景(中继器),我们需要一个设备既能连接别人的蓝牙设备(做主机),又能被手机连接配置(做从机,这个是主从一体的程序,在CH585EVT例程中有 CentPeri——主从一体例程,整合了主机例程和从机例程的功能同时运行
在这个 CentPeri程序基础上,我们加上双向串口透传功能,
当CH585作为主机连接从机时:串口收到的数据 -> 发送给从机;从机发来的数据 -> 串口打印。
当CH585作为从机被手机连接时:串口收到的数据 -> 发送给手机;手机发来的数据 -> 串口打印。
智能分配:程序自动判断当前的连接状态,将串口数据路由到正确的蓝牙链路。
一、创建串口处理函数
为了实现代码的兼容性,利用一个公共的串口处理模块 app_uart_common.c和app_uart_common.h。充当了 UART 硬件 与 蓝牙协议栈 之间的桥梁。

/* * 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博客
-
发送路径----PC发数据给蓝牙(UART -> BLE):
- 电脑的串口调试工具发送 -> CH58x 串口中断 (
UART3_IRQHandler) -> 存入app_uart_rx_fifo-> 置位uart_rx_flag。 - 主循环 (
app_uart_process) 发现标志位 -> 判断蓝牙已连接 -> 触发 TMOS 任务 (Central_UART_TO_BLE_SEND_EVT或UART_TO_BLE_SEND_EVT)。 - TMOS 任务(在
central.c或peripheral.c中)从app_uart_rx_fifo取出数据 -> 调用GATT_WriteNoRsp或Notify发送给BLE设备。
- 电脑的串口调试工具发送 -> CH58x 串口中断 (
-
接收路径----蓝牙发数据给PC (BLE -> UART):
- 蓝牙协议栈收到空中数据 -> 回调
on_bleuartServiceEvt-> 调用app_uart_tx_data-> 存入app_uart_tx_fifo。 - 主循环 (
app_uart_process) 轮询 -> 发现硬件串口有空闲 (R8_UART3_TFC) -> 从app_uart_tx_fifo取出数据写入硬件寄存器 -> 芯片通过 TX 引脚发出。
在公共处理处理串口程序上,最关键的地方就是智能分发逻辑
- 蓝牙协议栈收到空中数据 -> 回调

这里通过 Peripheral_IsConnected 和 Central_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); }


在这个任务事件中,最主要的就在获取到主机连接到从机之后,获取handle值,将串口发送的数据to_test_buffer,调用发送函数GATT_WriteNoRsp发送给从机设备。
2、主机连接上从机,主机接收从机发送的NOTIFY数据,在将数据通过蓝牙主机,发送到串口上
主机接收notify数据通常在 centralProcessGATTMsg 的 ATT_HANDLE_VALUE_NOTI

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: 这是一个枚举值,代表“已连接状态”。作为“状态查询接口”,可以让串口分配问题得以解决

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),从机强行发送通常会失败。


// 将串口收到的数据 -> 发送给蓝牙 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, ¬i, 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 *)¬i, 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, ¬i, 0); if(result != SUCCESS) { PRINT("R2:%02x\r\n", result); send_to_ble_state = SEND_TO_BLE_SEND_FAILED; GATT_bm_free((gattMsg_t *)¬i, 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将接收的数据发送出去。

//蓝牙服务事件回调 // 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。

bool Peripheral_IsConnected(void) { // 判断句柄是否有效 return (peripheralConnList.connHandle != GAP_CONNHANDLE_INIT); }
四. 移植实现步骤
如果想移植这串口功能放在自己的程序中,可以根据以下步骤
-
添加文件:
- 将
app_uart_common.c和对应的头文件加入工程。 - 确保包含
app_drv_fifo.c(FIFO 驱动)。
- 将
-
修改
main.c:- 在
main()函数中调用app_uart_init()。 - 在
Main_Circulation()(主循环) 中调用app_uart_process(),实现数据搬运的。
- 在
-
修改
central.c(主机任务):- 注册
Central_ProcessEvent。 - 添加
Central_UART_TO_BLE_SEND_EVT事件处理分支。 - 在
centralProcessGATTMsg中添加ATT_HANDLE_VALUE_NOTI处理,调用app_uart_tx_data。 - 连接成功后: 记得开启 MTU 交换和服务发现,找到对应的特征值句柄 (
centralCharHdl)。 - 添加
Central_IsConnected函数
- 注册
-
修改
peripheral.c(从机任务):- 调用
ble_uart_add_service(on_bleuartServiceEvt)注册透传服务。 - 添加
UART_TO_BLE_SEND_EVT事件处理分支。 - 添加
Peripheral_IsConnected函数
- 调用
-
配置 TMOS 任务 ID:
- 确保
centralTaskId和Peripheral_TaskID在app_uart_common.c中可以访问,让公共串口函数能发送事件。
浙公网安备 33010602011771号