CH582M蓝牙无感配对与TMOS框架下的RS485联动控制
文章目录
- 一. 开发路线与核心痛点
- 二.主机端 :精准过滤与 RSSI 距离判定
- 1. 溯源:特征码从哪里来?
- 2. 主机拦截:抓取特征码与 RSSI 测量
- 3. 避坑:切断无脑查找
- 三. 从机端 (Peripheral):TMOS 无阻塞挂载 RS485
- 1. 注册 TMOS 独立事件
- 2. 串口中断
- 3. 最终联动:协议解析与“蓝牙鉴权”结合
- 四. 总结
本文将详细复盘如何基于沁恒官方的 CH582M 蓝牙从机/主机 (Peripheral/Central) 官方例程 (EXAM),一步步剥离冗余代码,利用特征码与 RSSI 信号强度实现设备的近距离无感配对,并在官方的 TMOS 操作系统下无阻塞地挂载自定义 RS485 任务,最终实现“蓝牙配对成功后,才允许 RS485 通信控制”的安保联动逻辑。
一. 开发路线与核心痛点
起点:沁恒官方 BLE 协议栈 Central 与 Peripheral 默认例程。
终点:实现主机靠近从机(RSSI阈值判定)自动连接;从机在非阻塞状态下监听 485 串口,并根据当前的蓝牙连接决定是否响应 485 硬件控制指令。
二.主机端 :精准过滤与 RSSI 距离判定
默认的 Central 例程它会把环境中所有的蓝牙信号(手环、耳机、电视)都拉入扫描列表并尝试连接。这在实际工业应用中是极其致命的,会导致频繁的握手失败(报错 Reason: 31)。我们需要对其进行“阉割”和改造,实现基于特定特征码和物理距离(RSSI)的唯一配对。
1. 溯源:特征码从哪里来?
在让主机去寻找从机之前,我们必须先知道从机在“喊”什么。打开从机(Peripheral)工程的 peripheral.c,找到广播数据数组 advertData[]:
在沁恒的官方体系中,SIMPLEPROFILE_SERV_UUID 默认定义为 0xFFE0。由于蓝牙数据在内存中是小端模式(Little Endian)存放的,所以在空中的真实数据包里,它呈现的原始字节流就是 0xE0, 0xFF。这就是我们要让主机去抓取的核心特征码。
2. 主机拦截:抓取特征码与 RSSI 测量
明确了特征码后,我们打开主机(Central)的 central.c,找到处理扫描事件的回调分支 GAP_DEVICE_INFO_EVENT。在这里,我们重写逻辑:遍历空中抓到的数据包,比对 0xE0 0xFF,并加上信号强度 rssi >= -50(代表设备贴近)的严苛条件。
caseGAP_DEVICE_INFO_EVENT:{uint8_tis_our_device=FALSE;uint8_ti;// 1. 遍历广播数据包,寻找 0xE0 0xFF 特征码for(i=0;i<(pEvent->deviceInfo.dataLen-1);i++){if(pEvent->deviceInfo.pEvtData[i]==0xE0&&pEvent->deviceInfo.pEvtData[i+1]==0xFF){is_our_device=TRUE;break;}}// 2. 特征码吻合,且信号强度 RSSI >= -50 (距离极近) 时,才加入可连接列表if(is_our_device==TRUE&&pEvent->deviceInfo.rssi>=-50){PRINT("发现目标设备!准备连接... RSSI: %d\n",pEvent->deviceInfo.rssi);centralAddDeviceInfo(pEvent->deviceInfo.addr,pEvent->deviceInfo.addrType);}}break;3. 避坑:切断无脑查找
做完上面这一步还不够,很多开发者会发现主机依然在疯狂尝试连接未知设备。
原因在于:沁恒的官方代码在 centralEventCB 的尾部,留了 GAP_EXT_ADV_DEVICE_INFO_EVENT(扩展广播)和 GAP_DIRECT_DEVICE_INFO_EVENT(定向广播)两个分支。这两个分支没有任何过滤逻辑,会把环境里其他的杂散蓝牙设备无脑塞进列表(如果不将其注释屏蔽,不仅会引发无端的连接报错,大量无用的广播事件还会疯狂占用 TMOS 系统的调度资源,导致后续添加的自定义任务被严重阻塞)。
必须将这两个分支内部的代码彻底注释掉(焊死后门):
三. 从机端 (Peripheral):TMOS 无阻塞挂载 RS485
1. 注册 TMOS 独立事件
首先,在从机工程的 peripheral.c 中,我们需要为 485 任务申请一个专属的事件标志(注意不要和系统已有的宏冲突)。并在初始化函数中开启硬件和定时器。
// 1. 定义 485 定时处理事件 (0x0080 目前未被占用)#defineUART_PROCESS_EVT0x0080// 2. 在 Peripheral_Init() 的尾部,加入硬件初始化与任务启动GPIOPinRemap(ENABLE,RB_PIN_UART2);// 串口引脚重映射GPIOB_SetBits(GPIO_Pin_23);GPIOB_ModeCfg(GPIO_Pin_22,GPIO_ModeIN_PU);GPIOB_ModeCfg(GPIO_Pin_23,GPIO_ModeOut_PP_5mA);UART2_DefInit();UART2_BaudRateCfg(9600);UART2_ByteTrigCfg(UART_7BYTE_TRIG);UART2_INTCfg(ENABLE,RB_IER_RECV_RDY|RB_IER_LINE_STAT);PFIC_EnableIRQ(UART2_IRQn);// 核心:启动 TMOS 任务,约 10 毫秒后去检查一次串口状态tmos_start_task(Peripheral_TaskID,UART_PROCESS_EVT,16);2. 串口中断
为了不影响蓝牙底层的高优中断,我们的串口中断服务函数必须做到“快进快出”。中断里绝对不放任何协议解析逻辑,只负责把数据搬运到 RxBuff 数组,并给 TMOS 任务举起一个标志位(recv_flag = 1)。
串口中断放置在peripheral.c 最后就好。
__INTERRUPT __HIGH_CODEvoidUART2_IRQHandler(void){volatileuint8_ti;switch(UART2_GetITFlag()){caseUART_II_RECV_RDY:// 收到定长数据for(i=0;i!=trigB;i++){RxBuff[recv_len++]=UART2_RecvByte();}break;caseUART_II_RECV_TOUT:// 接收超时(一帧数据结束)i=UART2_RecvString(&RxBuff[recv_len]);recv_len+=i;recv_flag=1;// 举起标志位,告诉 TMOS 任务去解析break;}}3. 最终联动:协议解析与“蓝牙鉴权”结合
这是整个系统最后的一步。在 Peripheral_ProcessEvent (TMOS 事件调度中心)中,我们拦截刚才注册的 UART_PROCESS_EVT 事件。
不仅要校验 485 数据帧的合法性(包头、包尾、动态 Checksum),更要直接读取蓝牙协议栈的连接句柄 peripheralConnList.connHandle。只有当它不等于 GAP_CONNHANDLE_INIT(初始未连接状态)时,才证明设备已经通过了无线配对,此时才允许执行 485 下发的硬件动作。
// 拦截我们自定义的 485 处理事件if(events&UART_PROCESS_EVT){if(recv_flag==1){// 假设协议帧最短 5 字节:包头(AA) + 地址 + 功能码 + 校验和 + 包尾(55)if(recv_len>=5){if(RxBuff[0]==0xAA&&RxBuff[recv_len-1]==0x55){// 计算前面所有有效字节的校验和uint8_tmy_sum=Calc_Checksum(RxBuff,recv_len-2);if(my_sum==RxBuff[recv_len-2]){uint8_tdev_addr=RxBuff[1];uint8_tfunc_cmd=RxBuff[2];uint8_treply_buf[20];uint8_treply_len=0;// 💡💡 【核心防线:检查物理设备是否已完成蓝牙无线配对】 💡💡if(peripheralConnList.connHandle==GAP_CONNHANDLE_INIT){PRINT("警告:蓝牙未配对!直接静默,无视 485 指令!\n");// reply_len 保持为 0,设备如死机般毫无反应,彻底拒绝非法试探}else{// 蓝牙已配对,鉴权通过,开始拆包执行真实业务reply_buf[0]=0xBB;reply_buf[1]=dev_addr;reply_buf[2]=func_cmd;switch(func_cmd){case0x01:reply_buf[3]=0x01;// 业务执行成功状态位reply_len=6;PRINT("鉴权通过,已开启设备!\n");break;case0x02:reply_buf[3]=0x01;reply_len=6;PRINT("鉴权通过,已关闭设备!\n");break;}}// 只有鉴权通过且成功执行,才打包并回发响应帧if(reply_len>0){// 自动计算校验和并封包reply_buf[reply_len-2]=Calc_Checksum(reply_buf,reply_len-2);reply_buf[reply_len-1]=0x55;UART2_SendString(reply_buf,reply_len);}}}}// 清理现场,等待下一帧recv_len=0;recv_flag=0;}// 重新设定定时器,10毫秒后再次触发,形成非阻塞循环!tmos_start_task(Peripheral_TaskID,UART_PROCESS_EVT,16);// 清除本次事件标志,把 CPU 控制权还给蓝牙协议栈return(events^UART_PROCESS_EVT);}四. 总结
通过上述的底层改造,我们成功在 CH582M 官方默认例程上,构建了一套**“基于 RSSI 无感配对的高安全性网关”**。
利用蓝牙广播的特征码实现物理距离判定,利用 TMOS 的非阻塞机制实现了高频有线数据的监听,并通过对connHandle的跨层级读取,巧妙地将无线认证与有线控制结合在一起。既保证了蓝牙通信的极致稳定,又打造了无懈可击的安保防线,彻底杜绝了未经配对授权的恶意控制。希望本次复盘能为大家在开发类似网关设备时提供避坑参考。
