木子剑
生命不熄,学习不止!

第1步:打开EVT\EXAM\BLE\Peripheral\Profile下的gattprofile.c和gattprofile.h文件,

// Key Pressed UUID
#define SIMPLEPROFILE_CHAR1_UUID    0xFFE1
#define SIMPLEPROFILE_CHAR2_UUID    0xFFE2
#define SIMPLEPROFILE_CHAR3_UUID    0xFFE3
#define SIMPLEPROFILE_CHAR4_UUID    0xFFE4
#define SIMPLEPROFILE_CHAR5_UUID    0xFFE5

官方当前GARR属性表只有5张,0xFFE1 -- 0xFFE5,我们参考方法,实现0xFFE6.

最终效果如:

微信图片_20260713095542_4_1

只配了读取和写入权限,接下来通过手机写入0x0F关掉  0x0E打开,原理图上的PB6外接的LED灯.

在开始之前,先使用下面代码,测试LED是不是能正常点亮:

#include "CH58x_common.h"
// muzi测试led任务
static void MuZiLedTask(void)  // led函数TASK
{
    static uint8_t init_1=1;
    if(init_1) // 只初始化一次
    {
        init_1=0;
        GPIOB_ModeCfg(GPIO_Pin_6, GPIO_ModeOut_PP_20mA); // 推挽输出最大5mA
        GPIOB_InverseBits(GPIO_Pin_6); // PB6电平翻转,点亮LED
    }

    GPIOB_InverseBits(GPIO_Pin_6); // PB6电平翻转,点亮LED
}

如果裸机要在GPIOB_InverseBits(GPIO_Pin_6); 后面多加一段延时,才能看清LED灯闪.我是直接用的TMOS系统的周期性EV事件进行测试OK.

时间2026-08-26:

经过阅读及测试发现,用户名首先实现gattprofile.c,    gattprofile.h就可以。

以下是gattprofile.h

/********************************** (C) COPYRIGHT *******************************
 * File Name          : gattprofile.h
 * Author             : WCH
 * Version            : V1.0
 * Date               : 2018/12/11
 * Description        : Simple GATT Profile 头文件,定义 MuZi 协议特征值、UUID 及 API 接口
 *********************************************************************************
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * Attention: This software (modified or not) and binary are used for 
 * microcontroller manufactured by Nanjing Qinheng Microelectronics.
 *******************************************************************************/

#ifndef GATTPROFILE_H
#define GATTPROFILE_H

#ifdef __cplusplus
extern "C" {
#endif

/*********************************************************************
 * INCLUDES
 */

/*********************************************************************
 * CONSTANTS
 */

// Profile Parameters - 特征值参数 ID 定义(需与 gattprofile.c 中 switch-case 一一对应)
#define SIMPLEPROFILE_CHAR1         0           // RW uint8_t - 通用特征值 1(读写)
#define SIMPLEPROFILE_CHAR2         1           // RW uint8_t - 通用特征值 2(读写)
#define SIMPLEPROFILE_CHAR3         2           // RW uint8_t - 通用特征值 3(读写)
#define SIMPLEPROFILE_CHAR4         3           // RW uint8_t - 通用特征值 4(读写),MuZi 协议复用为 ACK/响应通知通道
#define SIMPLEPROFILE_CHAR5         4           // RW uint8_t[5] - 通用特征值 5(读写)
#define MuZi_CHAR6                  5           // MuZi 命令特征值(Write Only),主机→从机:下发控制指令(采集/增益/模式切换等)
#define MuZi_CHAR7                  6           // MuZi 数据特征值(Notify Only),从机→主机:上报 ADC 分片数据等

// Simple Profile Service UUID - 自定义服务 UUID
#define SIMPLEPROFILE_SERV_UUID     0xFFE0

// Characteristic UUIDs - 各特征值 UUID
#define SIMPLEPROFILE_CHAR1_UUID    0xFFE1      // 通用特征值 1 UUID
#define SIMPLEPROFILE_CHAR2_UUID    0xFFE2      // 通用特征值 2 UUID
#define SIMPLEPROFILE_CHAR3_UUID    0xFFE3      // 通用特征值 3 UUID
#define SIMPLEPROFILE_CHAR4_UUID    0xFFE4      // 通用特征值 4 UUID,MuZi 协议复用为 ACK/响应通道
#define SIMPLEPROFILE_CHAR5_UUID    0xFFE5      // 通用特征值 5 UUID

#define MuZi_CHAR6_UUID             0xFFE6      // MuZi 命令通道 UUID(主机写入指令)
#define MuZi_CHAR7_UUID             0xFFE7      // MuZi 数据通道 UUID(从机通知 ADC 数据)

// Simple Keys Profile Services bit fields - 服务注册位掩码
#define SIMPLEPROFILE_SERVICE       0x00000001  // SimpleProfile 服务使能位

// Length of characteristic in bytes (Default MTU is 23) - 各特征值最大字节长度
#define SIMPLEPROFILE_CHAR1_LEN     1           // CHAR1 长度:1 字节
#define SIMPLEPROFILE_CHAR2_LEN     1           // CHAR2 长度:1 字节
#define SIMPLEPROFILE_CHAR3_LEN     1           // CHAR3 长度:1 字节
#define SIMPLEPROFILE_CHAR4_LEN     1           // CHAR4 长度:1 字节(MuZi ACK 帧实际使用 4~6 字节,受 MTU 限制)
#define SIMPLEPROFILE_CHAR5_LEN     5           // CHAR5 长度:5 字节
                                                // 以上长度用于声明 GATT 属性表中特征值缓冲区大小
#define MuZi_CHAR6_LEN              12          // MuZi 命令帧最大长度:12 字节(帧头1 + 命令码1 + 载荷长度1 + 载荷最多8 + CRC1)
#define MuZi_CHAR7_LEN              240         // MuZi 数据分片最大长度:240 字节(配合 BLE_BUFF_MAX_LEN=247,扣除 ATT 头3 + 协议开销4 = 240 有效载荷)

/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * Profile Callbacks
 */

// Callback when a characteristic value has changed - 特征值写入回调函数类型
// paramID: 被写入的特征值参数 ID(SIMPLEPROFILE_CHAR1~MuZi_CHAR7)
// pValue:  写入数据指针
// len:     写入数据长度
typedef void (*simpleProfileChange_t)(uint8_t paramID, uint8_t *pValue, uint16_t len);

typedef struct
{
    simpleProfileChange_t pfnSimpleProfileChange; // 特征值变更回调函数指针,由应用层注册
} simpleProfileCBs_t;

/*********************************************************************
 * API FUNCTIONS - 对外 API 接口声明
 */

/*
 * SimpleProfile_AddService - 初始化 Simple GATT Profile 服务,向 GATT Server 注册属性表
 *
 * @param   services - 要注册的服务位掩码,可包含多个服务的按位或组合
 * @return  bStatus_t - SUCCESS 或错误码
 */
extern bStatus_t SimpleProfile_AddService(uint32_t services);

/*
 * SimpleProfile_RegisterAppCBs - 注册应用层回调函数(全局仅调用一次)
 *
 * @param   appCallbacks - 应用回调结构体指针,包含特征值变更回调
 * @return  bStatus_t - SUCCESS 或 bleAlreadyInRequestedMode(重复注册)
 */
extern bStatus_t SimpleProfile_RegisterAppCBs(simpleProfileCBs_t *appCallbacks);

/*
 * SimpleProfile_SetParameter - 设置 Simple GATT Profile 特征值参数
 *
 * @param   param - 特征值参数 ID(SIMPLEPROFILE_CHAR1~MuZi_CHAR7)
 * @param   len   - 写入数据长度(必须与该特征值定义的 LEN 匹配)
 * @param   value - 写入数据指针(根据 param ID 自动转换为对应数据类型)
 * @return  bStatus_t - SUCCESS 或 bleInvalidRange(参数 ID 无效)
 */
extern bStatus_t SimpleProfile_SetParameter(uint8_t param, uint16_t len, void *value);

/*
 * SimpleProfile_GetParameter - 读取 Simple GATT Profile 特征值参数
 *
 * @param   param - 特征值参数 ID(SIMPLEPROFILE_CHAR1~MuZi_CHAR7)
 * @param   value - 读出数据指针(根据 param ID 自动转换为对应数据类型)
 * @return  bStatus_t - SUCCESS 或 bleInvalidRange(参数 ID 无效)
 */
extern bStatus_t SimpleProfile_GetParameter(uint8_t param, void *value);

/*
 * simpleProfile_Notify - 通过 CHAR4 发送通知(MuZi 协议复用为 ACK/响应通道)
 *
 * @param   connHandle - 当前连接句柄
 * @param   pNoti      - 通知数据结构体指针(含长度和数据指针,数据需由 GATT_bm_alloc 分配)
 * @return  bStatus_t - SUCCESS 或发送失败错误码
 */
extern bStatus_t simpleProfile_Notify(uint16_t connHandle, attHandleValueNoti_t *pNoti);

/*
 * simpleProfile_Notify7 - 通过 MuZi CHAR7 (UUID=FFE7) 发送 ADC 数据通知
 *
 * @param   connHandle - 当前连接句柄
 * @param   pNoti      - 通知数据结构体指针(含长度和数据指针)
 * @return  bStatus_t - SUCCESS 或发送失败错误码
 */
extern bStatus_t simpleProfile_Notify7(uint16_t connHandle, attHandleValueNoti_t *pNoti);


/*********************************************************************
*********************************************************************/

#ifdef __cplusplus
}
#endif

#endif /* GATTPROFILE_H */

 

以下是gattprofile.c文件

/********************************** (C) COPYRIGHT *******************************
 * File Name          : gattprofile.C
 * Author             : WCH
 * Version            : V1.0 
 * Date               : 2018/12/10
 * Description        : Customize services with five different attributes, 
 *                      including readable, writable, notification, 
 *                      readable and writable, and safe readable
 *                      (自定义 GATT 服务,包含可读、可写、通知、读写及安全读等属性,
 *                       并扩展了 MuZi 协议的命令(CHAR6)和数据(CHAR7)通道)
 *********************************************************************************
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * Attention: This software (modified or not) and binary are used for 
 * microcontroller manufactured by Nanjing Qinheng Microelectronics.
 *******************************************************************************/

/*********************************************************************
 * INCLUDES (头文件引用)
 */
#include "CONFIG.h"
#include "gattprofile.h"

/*********************************************************************
 * MACROS (宏定义)
 */

/*********************************************************************
 * CONSTANTS (常量定义)
 */

// Position of simpleProfilechar4 value in attribute array
// CHAR4 值在属性表中的索引位置(用于 Notify 时快速定位句柄)
#define SIMPLEPROFILE_CHAR4_VALUE_POS    11

// Position of MuZi char7 value in attribute array (after adding CCCD)
// MuZi CHAR7 值在属性表中的索引位置(用于 Notify7 时快速定位句柄)
// AttrTbl Index (属性表索引布局):
// 0: Service (服务声明)
// 1-3: Char1 (声明+值+描述)
// 4-6: Char2 (声明+值+描述)
// 7-9: Char3 (声明+值+描述)
// 10-13: Char4 (10:Decl, 11:Val, 12:CCCD, 13:Desc)
// 14-16: Char5 (声明+值+描述)
// 17-19: Char6 (声明+值+描述)
// 20-23: Char7 (20:Decl, 21:Val, 22:CCCD, 23:Desc)
#define MuZi_CHAR7_VALUE_POS             21

/*********************************************************************
 * TYPEDEFS (类型定义)
 */

/*********************************************************************
 * GLOBAL VARIABLES (全局变量 - UUID 定义)
 */
// Simple GATT Profile Service UUID: 0xFFE0 (主服务 UUID,小端序存储)
const uint8_t simpleProfileServUUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(SIMPLEPROFILE_SERV_UUID), HI_UINT16(SIMPLEPROFILE_SERV_UUID)};

// Characteristic 1 UUID: 0xFFE1 (通用特征值 1)
const uint8_t simpleProfilechar1UUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(SIMPLEPROFILE_CHAR1_UUID), HI_UINT16(SIMPLEPROFILE_CHAR1_UUID)};

// Characteristic 2 UUID: 0xFFE2 (通用特征值 2)
const uint8_t simpleProfilechar2UUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(SIMPLEPROFILE_CHAR2_UUID), HI_UINT16(SIMPLEPROFILE_CHAR2_UUID)};

// Characteristic 3 UUID: 0xFFE3 (通用特征值 3)
const uint8_t simpleProfilechar3UUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(SIMPLEPROFILE_CHAR3_UUID), HI_UINT16(SIMPLEPROFILE_CHAR3_UUID)};

// Characteristic 4 UUID: 0xFFE4 (MuZi 协议复用为 ACK/响应通知通道)
const uint8_t simpleProfilechar4UUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(SIMPLEPROFILE_CHAR4_UUID), HI_UINT16(SIMPLEPROFILE_CHAR4_UUID)};

// Characteristic 5 UUID: 0xFFE5 (通用特征值 5)
const uint8_t simpleProfilechar5UUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(SIMPLEPROFILE_CHAR5_UUID), HI_UINT16(SIMPLEPROFILE_CHAR5_UUID)};

// MuZi CHAR6 UUID: 0xFFE6 (命令下行通道,主机→从机写入控制指令)
const uint8_t MuZi_char6UUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(MuZi_CHAR6_UUID), HI_UINT16(MuZi_CHAR6_UUID)};

// MuZi CHAR7 UUID: 0xFFE7 (数据上行通道,从机→主机通知 ADC 分片数据)
const uint8_t MuZi_char7UUID[ATT_BT_UUID_SIZE] = {
    LO_UINT16(MuZi_CHAR7_UUID), HI_UINT16(MuZi_CHAR7_UUID)};

/*********************************************************************
 * EXTERNAL VARIABLES (外部变量)
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS (外部函数)
 */

/*********************************************************************
 * LOCAL VARIABLES (局部静态变量)
 */

// 应用层回调函数指针(特征值被写入时通过此回调通知应用层处理)
static simpleProfileCBs_t *simpleProfile_AppCBs = NULL;

/*********************************************************************
 * Profile Attributes - variables (配置文件属性变量)
 */

// Simple Profile Service attribute (服务声明属性,标识本服务的 UUID)
static const gattAttrType_t simpleProfileService = {ATT_BT_UUID_SIZE, simpleProfileServUUID};

// ===== Characteristic 1: Read/Write (可读可写) =====
// 特征值 1 属性位掩码:支持读和写操作
static uint8_t simpleProfileChar1Props = GATT_PROP_READ | GATT_PROP_WRITE;
// 特征值 1 本地数据缓冲区(存储当前值,供读取和 SetParameter 使用)
static uint8_t simpleProfileChar1[SIMPLEPROFILE_CHAR1_LEN] = {0};
// 特征值 1 用户描述字符串(GATT 可选属性,便于调试工具识别)
static uint8_t simpleProfileChar1UserDesp[] = "Characteristic 1\0";

// ===== Characteristic 2: Read Only (只读) =====
// 特征值 2 属性位掩码:仅支持读操作
static uint8_t simpleProfileChar2Props = GATT_PROP_READ;
// 特征值 2 本地数据缓冲区
static uint8_t simpleProfileChar2[SIMPLEPROFILE_CHAR2_LEN] = {0};
// 特征值 2 用户描述字符串
static uint8_t simpleProfileChar2UserDesp[] = "Characteristic 2\0";

// ===== Characteristic 3: Write Only (只写) =====
// 特征值 3 属性位掩码:仅支持写操作
static uint8_t simpleProfileChar3Props = GATT_PROP_WRITE;
// 特征值 3 本地数据缓冲区
static uint8_t simpleProfileChar3[SIMPLEPROFILE_CHAR3_LEN] = {0};
// 特征值 3 用户描述字符串
static uint8_t simpleProfileChar3UserDesp[] = "Characteristic 3\0";

// ===== Characteristic 4: Notify (通知,MuZi 复用为 ACK/响应通道) =====
// 特征值 4 属性位掩码:仅支持通知(不可直接读写)
static uint8_t simpleProfileChar4Props = GATT_PROP_NOTIFY;
// 特征值 4 本地数据缓冲区(Notify 不通过此缓冲发送,仅作占位)
static uint8_t simpleProfileChar4[SIMPLEPROFILE_CHAR4_LEN] = {0};
// 特征值 4 CCCD 配置表(每个连接独立维护通知使能状态)
// Each client has its own instantiation of the Client Characteristic Configuration.
// Reads/writes only affect the configuration for that specific client.
static gattCharCfg_t simpleProfileChar4Config[PERIPHERAL_MAX_CONNECTION];

// MuZi CHAR7 CCCD 配置表(每个连接独立维护数据通知使能状态)
static gattCharCfg_t simpleProfileChar7Config[PERIPHERAL_MAX_CONNECTION];

// 特征值 4 用户描述字符串
static uint8_t simpleProfileChar4UserDesp[] = "Characteristic 4\0";

// ===== Characteristic 5: Authenticated Read (认证读) =====
// 特征值 5 属性位掩码:需配对认证后才能读取
static uint8_t simpleProfileChar5Props = GATT_PROP_READ;
// 特征值 5 本地数据缓冲区
static uint8_t simpleProfileChar5[SIMPLEPROFILE_CHAR5_LEN] = {0};
// 特征值 5 用户描述字符串
static uint8_t simpleProfileChar5UserDesp[] = "Characteristic 5\0";

// ===== MuZi Characteristic 6: Write Only (命令下行通道) =====
// MuZi CHAR6 属性位掩码:仅支持写入(主机下发控制指令)
static uint8_t MuZi_Char6Props = GATT_PROP_WRITE;  
// MuZi CHAR6 本地数据缓冲区(初始化为帧头 0xAA,便于调试识别)
static uint8_t MuZi_Char6[MuZi_CHAR6_LEN] = {0xAA};
// MuZi CHAR6 用户描述字符串
static uint8_t MuZi_Char6UserDesp[] = "MuZi_Characteristic 6\0";

// ===== MuZi Characteristic 7: Notify (数据上行通道) =====
// MuZi CHAR7 属性位掩码:仅支持通知(从机上报 ADC 分片数据)
static uint8_t MuZi_Char7Props = GATT_PROP_NOTIFY;
// MuZi CHAR7 本地数据缓冲区(初始化为 SOF 0x55,便于调试识别)
static uint8_t MuZi_Char7[MuZi_CHAR7_LEN] = {0x55};
// MuZi CHAR7 用户描述字符串
static uint8_t MuZi_Char7UserDesp[] = "MuZi_Characteristic 7\0";

/*********************************************************************
 * Profile Attributes - Table (GATT 属性表)
 * 格式: {UUID类型/权限/自动句柄/值指针}
 * 注意:属性表顺序必须与上方 POS 常量定义的索引严格对应
 */
static gattAttribute_t simpleProfileAttrTbl[] = {
    // ===== Simple Profile Service Declaration (服务声明,索引 0) =====
    { { ATT_BT_UUID_SIZE, primaryServiceUUID   }, GATT_PERMIT_READ,                   0, (uint8_t *)&simpleProfileService      },

    // ----- Characteristic 1: Read/Write + User Desc (索引 1-3) -----
    { { ATT_BT_UUID_SIZE, characterUUID        }, GATT_PERMIT_READ,                   0, &simpleProfileChar1Props              }, // [1] 特征值声明(描述属性位掩码)
    { { ATT_BT_UUID_SIZE, simpleProfilechar1UUID}, GATT_PERMIT_READ | GATT_PERMIT_WRITE , 0, simpleProfileChar1                }, // [2] 特征值本体(可读可写)
    { { ATT_BT_UUID_SIZE, charUserDescUUID     }, GATT_PERMIT_READ,                   0, simpleProfileChar1UserDesp            }, // [3] 用户描述

    // ----- Characteristic 2: Read Only + User Desc (索引 4-6) -----
    { { ATT_BT_UUID_SIZE, characterUUID        }, GATT_PERMIT_READ,                   0, &simpleProfileChar2Props              }, // [4] 特征值声明
    { { ATT_BT_UUID_SIZE, simpleProfilechar2UUID}, GATT_PERMIT_READ,                  0, simpleProfileChar2                    }, // [5] 特征值本体(只读)
    { { ATT_BT_UUID_SIZE, charUserDescUUID     }, GATT_PERMIT_READ,                   0, simpleProfileChar2UserDesp            }, // [6] 用户描述

    // ----- Characteristic 3: Write Only + User Desc (索引 7-9) -----
    { { ATT_BT_UUID_SIZE, characterUUID        }, GATT_PERMIT_READ,                   0, &simpleProfileChar3Props              }, // [7] 特征值声明(声明本身始终可读)
    { { ATT_BT_UUID_SIZE, simpleProfilechar3UUID}, GATT_PERMIT_WRITE,                 0, simpleProfileChar3                    }, // [8] 特征值本体(只写)
    { { ATT_BT_UUID_SIZE, charUserDescUUID     }, GATT_PERMIT_READ,                   0, simpleProfileChar3UserDesp            }, // [9] 用户描述

    // ----- Characteristic 4: Notify + CCCD + User Desc (索引 10-13) -----
    { { ATT_BT_UUID_SIZE, characterUUID        }, GATT_PERMIT_READ,                   0, &simpleProfileChar4Props              }, // [10] 特征值声明
    { { ATT_BT_UUID_SIZE, simpleProfilechar4UUID}, 0,                                 0, simpleProfileChar4                    }, // [11] 特征值本体(Notify 无直接读写权限,POS=11)
    { { ATT_BT_UUID_SIZE, clientCharCfgUUID    }, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, (uint8_t *)simpleProfileChar4Config }, // [12] CCCD(客户端通知使能配置)
    { { ATT_BT_UUID_SIZE, charUserDescUUID     }, GATT_PERMIT_READ,                   0, simpleProfileChar4UserDesp            }, // [13] 用户描述

    // ----- Characteristic 5: Authenticated Read + User Desc (索引 14-16) -----
    { { ATT_BT_UUID_SIZE, characterUUID        }, GATT_PERMIT_READ,                   0, &simpleProfileChar5Props              }, // [14] 特征值声明
    { { ATT_BT_UUID_SIZE, simpleProfilechar5UUID}, GATT_PERMIT_AUTHEN_READ,           0, simpleProfileChar5                    }, // [15] 特征值本体(需配对认证后才可读)
    { { ATT_BT_UUID_SIZE, charUserDescUUID     }, GATT_PERMIT_READ,                   0, simpleProfileChar5UserDesp            }, // [16] 用户描述

    // ----- MuZi CHAR6: Write Only (命令通道) + User Desc (索引 17-19) -----
    { { ATT_BT_UUID_SIZE, characterUUID        }, GATT_PERMIT_READ,                   0, &MuZi_Char6Props                      }, // [17] 特征值声明
    { { ATT_BT_UUID_SIZE, MuZi_char6UUID       }, GATT_PERMIT_WRITE,                  0, MuZi_Char6                            }, // [18] 特征值本体(主机写入 MuZi 命令帧)
    { { ATT_BT_UUID_SIZE, charUserDescUUID     }, GATT_PERMIT_READ,                   0, MuZi_Char6UserDesp                    }, // [19] 用户描述

    // ----- MuZi CHAR7: Notify + CCCD + User Desc (索引 20-23) -----
    { { ATT_BT_UUID_SIZE, characterUUID        }, GATT_PERMIT_READ,                   0, &MuZi_Char7Props                      }, // [20] 特征值声明
    { { ATT_BT_UUID_SIZE, MuZi_char7UUID       }, 0,                                  0, MuZi_Char7                            }, // [21] 特征值本体(Notify-only,POS=21)
    { { ATT_BT_UUID_SIZE, clientCharCfgUUID    }, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, (uint8_t *)simpleProfileChar7Config }, // [22] CCCD(MuZi 数据通知使能配置)
    { { ATT_BT_UUID_SIZE, charUserDescUUID     }, GATT_PERMIT_READ,                   0, MuZi_Char7UserDesp                    }, // [23] 用户描述
};

/*********************************************************************
 * LOCAL FUNCTIONS (局部函数原型)
 */
// GATT 读属性回调(客户端发起 Read Request 时由协议栈调用)
static bStatus_t simpleProfile_ReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
                                          uint8_t *pValue, uint16_t *pLen, uint16_t offset, uint16_t maxLen, uint8_t method);

// GATT 写属性回调(客户端发起 Write Request 时由协议栈调用)
static bStatus_t simpleProfile_WriteAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
                                           uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method);

// 链路连接状态变更回调(断开时重置 CCCD)
static void simpleProfile_HandleConnStatusCB(uint16_t connHandle, uint8_t changeType);

/*********************************************************************
 * PROFILE CALLBACKS (协议栈回调注册)
 */
// Simple Profile Service Callbacks (GATT 服务回调结构体)
static gattServiceCBs_t simpleProfileCBs = {
    simpleProfile_ReadAttrCB,  // Read callback function pointer (读属性回调)
    simpleProfile_WriteAttrCB, // Write callback function pointer (写属性回调)
    NULL                       // Authorization callback function pointer (授权回调,未使用)
};

/*********************************************************************
 * PUBLIC FUNCTIONS (公开函数)
 */

/*********************************************************************
 * @fn      SimpleProfile_AddService
 *
 * @brief   Initializes the Simple Profile service by registering
 *          GATT attributes with the GATT server.
 *          (初始化 Simple Profile 服务,向 GATT 服务器注册属性表)
 *
 * @param   services - services to add. This is a bit map and can
 *                     contain more than one service.
 *                     (要注册的服务位掩码,支持多服务按位或组合)
 *
 * @return  Success or Failure
 */
bStatus_t SimpleProfile_AddService(uint32_t services)
{
    bStatus_t status = SUCCESS;

    // Initialize CCCD to default (notifications disabled)
    // 初始化所有 CCCD 为默认状态(通知关闭),防止未使能就发送通知
    GATTServApp_InitCharCfg(INVALID_CONNHANDLE, simpleProfileChar4Config);
    GATTServApp_InitCharCfg(INVALID_CONNHANDLE, simpleProfileChar7Config);

    // Register connection status callback with Link DB
    // 注册链路状态回调,用于连接断开时自动重置 CCCD
    linkDB_Register(simpleProfile_HandleConnStatusCB);

    // Register GATT service only if requested
    // 仅当请求了 SIMPLEPROFILE_SERVICE 时才注册属性表
    if (services & SIMPLEPROFILE_SERVICE)
    {
        status = GATTServApp_RegisterService(simpleProfileAttrTbl,
                                             GATT_NUM_ATTRS(simpleProfileAttrTbl),  // 属性表及条目数
                                             GATT_MAX_ENCRYPT_KEY_SIZE,             // 最大加密密钥长度
                                             &simpleProfileCBs);                    // 服务回调结构体
    }

    return status;
}

/*********************************************************************
 * @fn      SimpleProfile_RegisterAppCBs
 *
 * @brief   Registers the application callback function. Only call
 *          this function once.
 *          (注册应用层回调函数,特征值被写入时通知应用层。全局仅调用一次)
 *
 * @param   callbacks - pointer to application callbacks.
 *                      (应用回调结构体指针)
 *
 * @return  SUCCESS or bleAlreadyInRequestedMode
 */
bStatus_t SimpleProfile_RegisterAppCBs(simpleProfileCBs_t *appCallbacks)
{
    if (appCallbacks != NULL)
    {
        simpleProfile_AppCBs = appCallbacks;
        return SUCCESS;
    }

    return bleAlreadyInRequestedMode; // 传入 NULL 视为重复/无效注册
}


/*********************************************************************
 * @fn      SimpleProfile_SetParameter (设置参数)
 *
 * @brief   Set a Simple Profile parameter.
 *          (设置指定特征值的本地数据副本,不影响 BLE 空中接口)
 *
 * @param   param - Profile parameter ID (特征值参数 ID)
 * @param   len - length of data to write (写入数据长度,必须匹配特征值定义长度)
 * @param   value - pointer to data to write. (写入数据指针)
 *
 * @return  bStatus_t - SUCCESS / bleInvalidRange / INVALIDPARAMETER
 */
bStatus_t SimpleProfile_SetParameter(uint8_t param, uint16_t len, void *value)
{
    bStatus_t ret = SUCCESS;
    switch(param)
    {
        case SIMPLEPROFILE_CHAR1:
            if(len == SIMPLEPROFILE_CHAR1_LEN)
                tmos_memcpy(simpleProfileChar1, value, SIMPLEPROFILE_CHAR1_LEN);
            else
                ret = bleInvalidRange; // 长度不匹配
            break;

        case SIMPLEPROFILE_CHAR2:
            if(len == SIMPLEPROFILE_CHAR2_LEN)
                tmos_memcpy(simpleProfileChar2, value, SIMPLEPROFILE_CHAR2_LEN);
            else
                ret = bleInvalidRange;
            break;

        case SIMPLEPROFILE_CHAR3:
            if(len == SIMPLEPROFILE_CHAR3_LEN)
                tmos_memcpy(simpleProfileChar3, value, SIMPLEPROFILE_CHAR3_LEN);
            else
                ret = bleInvalidRange;
            break;

        case SIMPLEPROFILE_CHAR4:
            if(len == SIMPLEPROFILE_CHAR4_LEN)
                tmos_memcpy(simpleProfileChar4, value, SIMPLEPROFILE_CHAR4_LEN);
            else
                ret = bleInvalidRange;
            break;

        case SIMPLEPROFILE_CHAR5:
            if(len == SIMPLEPROFILE_CHAR5_LEN)
                tmos_memcpy(simpleProfileChar5, value, SIMPLEPROFILE_CHAR5_LEN);
            else
                ret = bleInvalidRange;
            break;

        case MuZi_CHAR6:  //-- MuZi 命令通道:设置本地命令缓冲副本
            if(len == MuZi_CHAR6_LEN)
                tmos_memcpy(MuZi_Char6, value, MuZi_CHAR6_LEN);
            else
                ret = bleInvalidRange;
            break;

        default:
            ret = INVALIDPARAMETER; // 未知参数 ID
            break;
    }

    return (ret);
}


/*********************************************************************
 * @fn      SimpleProfile_GetParameter (获取参数)
 *
 * @brief   Get a Simple Profile parameter.
 *          (获取指定特征值的本地数据副本)
 *
 * @param   param - Profile parameter ID (特征值参数 ID)
 * @param   value - pointer to data buffer to receive value. (读出数据缓冲指针)
 *
 * @return  bStatus_t - SUCCESS / INVALIDPARAMETER
 */
bStatus_t SimpleProfile_GetParameter(uint8_t param, void *value)
{
    bStatus_t ret = SUCCESS;
    switch(param)
    {
        case SIMPLEPROFILE_CHAR1:
            tmos_memcpy(value, simpleProfileChar1, SIMPLEPROFILE_CHAR1_LEN);
            break;

        case SIMPLEPROFILE_CHAR2:
            tmos_memcpy(value, simpleProfileChar2, SIMPLEPROFILE_CHAR2_LEN);
            break;

        case SIMPLEPROFILE_CHAR3:
            tmos_memcpy(value, simpleProfileChar3, SIMPLEPROFILE_CHAR3_LEN);
            break;

        case SIMPLEPROFILE_CHAR4:
            tmos_memcpy(value, simpleProfileChar4, SIMPLEPROFILE_CHAR4_LEN);
            break;

        case SIMPLEPROFILE_CHAR5:
            tmos_memcpy(value, simpleProfileChar5, SIMPLEPROFILE_CHAR5_LEN);
            break;

        case MuZi_CHAR6:  //-- MuZi 命令通道:读取本地命令缓冲副本
            tmos_memcpy(value, MuZi_Char6, MuZi_CHAR6_LEN);
            break;

        case MuZi_CHAR7:  //-- MuZi 数据通道:读取本地数据缓冲副本
            tmos_memcpy(value, MuZi_Char7, MuZi_CHAR7_LEN);
            break;

        default:
            ret = INVALIDPARAMETER; // 未知参数 ID
            break;
    }

    return (ret);
}


/*********************************************************************
 * @fn          simpleProfile_Notify (CHAR4 通知功能)
 *
 * @brief       Send a notification containing a heart rate measurement.
 *              (通过 CHAR4 发送通知数据,MuZi 协议复用为 ACK/响应通道)
 *
 * @param       connHandle - connection handle (当前连接句柄)
 * @param       pNoti - pointer to notification structure (通知结构体,含数据指针和长度)
 *
 * @return      Success or Failure (bleIncorrectMode 表示客户端未使能通知)
 */
bStatus_t simpleProfile_Notify(uint16_t connHandle, attHandleValueNoti_t *pNoti)
{   
    // 读取该连接的 CCCD 配置,检查客户端是否已使能通知
    uint16_t value = GATTServApp_ReadCharCfg(connHandle, simpleProfileChar4Config);

    // If notifications enabled (如果已开启通知)
    if(value & GATT_CLIENT_CFG_NOTIFY)
    {
        // Set the handle (通过预定义索引获取 CHAR4 值属性的运行时句柄)
        pNoti->handle = simpleProfileAttrTbl[SIMPLEPROFILE_CHAR4_VALUE_POS].handle;

        // Send the notification (通过 BLE 协议栈发送通知,FALSE=不需要认证)
        return GATT_Notification(connHandle, pNoti, FALSE);
    }
    return bleIncorrectMode; // 客户端未使能 CCCD 通知
}


/*********************************************************************
 * @fn          simpleProfile_Notify7 (CHAR7 MuZi 数据通知功能)
 *
 * @brief       Send a notification containing the MuZi CHAR7 data.
 *              (通过 MuZi CHAR7 发送 ADC 分片数据通知)
 *
 * @param       connHandle - connection handle (当前连接句柄)
 * @param       pNoti - pointer to notification structure (通知结构体,含数据指针和长度)
 *
 * @return      Success or Failure (bleIncorrectMode 表示客户端未使能通知)
 */
bStatus_t simpleProfile_Notify7(uint16_t connHandle, attHandleValueNoti_t *pNoti)
{
    // 读取该连接的 CHAR7 CCCD 配置
    uint16_t value = GATTServApp_ReadCharCfg(connHandle, simpleProfileChar7Config);

    // If notifications enabled (如果已开启 MuZi 数据通知)
    if(value & GATT_CLIENT_CFG_NOTIFY)
    {
        // Set the handle using the defined POS constant
        // 通过预定义索引获取 CHAR7 值属性的运行时句柄
        pNoti->handle = simpleProfileAttrTbl[MuZi_CHAR7_VALUE_POS].handle;
        
        // Send the notification (通过 BLE 协议栈发送通知)
        return GATT_Notification(connHandle, pNoti, FALSE);
    }
    
    return bleIncorrectMode; // 客户端未使能 CCCD 通知
}


/*********************************************************************
 * @fn          simpleProfile_ReadAttrCB (用户手机读取回调函数)
 *
 * @brief       Read an attribute.
 *              (GATT 读操作回调,当客户端发起 Read Request 时由协议栈调用)
 *
 * @param       connHandle - connection handle (当前连接句柄)
 * @param       pAttr - pointer to attribute being read (被读的属性指针)
 * @param       pValue - output buffer for read data (读出数据输出缓冲)
 * @param       pLen - output: actual bytes read (实际读出字节数)
 * @param       offset - read offset (supports Read Blob) (读偏移,支持 Read Blob 分包读取)
 * @param       maxLen - maximum bytes client can accept (客户端 MTU 限制的最大可读字节数)
 *
 * @return      Success or Failure
 */
static bStatus_t simpleProfile_ReadAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
                                          uint8_t *pValue, uint16_t *pLen, uint16_t offset,
                                          uint16_t maxLen, uint8_t method)
{
    // Only 16-bit UUIDs are used in Simple Profile (本服务仅使用 16-bit UUID)
    if (pAttr->type.len != ATT_BT_UUID_SIZE)
    {
        *pLen = 0;
        return ATT_ERR_INVALID_HANDLE;
    }

    // 从属性中提取 16-bit UUID 并映射到对应的特征值
    uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]);
    uint16_t attrLen = 0;
    uint8_t  charIndex = 0; // 特征值索引(可用于扩展统计或访问控制)

    switch (uuid)
    {
        // Char3 has no read permission; handled by GATT Server App before reaching here
        // CHAR3 为只写,协议栈会在到达此回调前拦截非法读请求
        case SIMPLEPROFILE_CHAR1_UUID:
            attrLen = SIMPLEPROFILE_CHAR1_LEN;
            charIndex = SIMPLEPROFILE_CHAR1;
            break;

        case SIMPLEPROFILE_CHAR2_UUID:
            attrLen = SIMPLEPROFILE_CHAR2_LEN;
            charIndex = SIMPLEPROFILE_CHAR2;
            break;

        // Char4 is not readable per spec, but included for notification state query
        // CHAR4 规范上不可读,但保留分支以兼容某些客户端的状态查询行为
        case SIMPLEPROFILE_CHAR4_UUID:
            attrLen = SIMPLEPROFILE_CHAR4_LEN;
            charIndex = SIMPLEPROFILE_CHAR4;
            break;

        case SIMPLEPROFILE_CHAR5_UUID:
            attrLen = SIMPLEPROFILE_CHAR5_LEN;
            charIndex = SIMPLEPROFILE_CHAR5;
            break;

        case MuZi_CHAR6_UUID:         // MuZi 命令通道(通常为只写,保留读分支用于调试回读)
            attrLen = MuZi_CHAR6_LEN;
            charIndex = MuZi_CHAR6;
            break;

        // MuZi_CHAR7 已改为 Notify-only,不再支持 Read,移除读分支
        // 未匹配的 UUID 返回属性未找到错误
        default:
            *pLen = 0;
            return ATT_ERR_ATTR_NOT_FOUND;
    }

    // 偏移校验:支持 Read Blob Request,offset 必须在有效范围内
    // 规范要求 offset >= attrLen 时返回 Invalid Offset
    if (offset >= attrLen)
    {
        *pLen = 0;
        return ATT_ERR_INVALID_OFFSET;
    }

    // Clamp to maxLen and copy attribute value from offset
    // 计算剩余可读字节数,截取不超过 maxLen 的数据并从偏移处拷贝
    uint16_t remaining = attrLen - offset;
    *pLen = (maxLen < remaining) ? maxLen : remaining;
    tmos_memcpy(pValue, pAttr->pValue + offset, *pLen);   //-- 将特征值数据填入读出缓冲

    return SUCCESS;
}


/*********************************************************************
 * @fn      simpleProfile_WriteAttrCB (用户手机写入回调函数)
 *
 * @brief   Validate attribute data prior to a write operation
 *          (GATT 写操作回调,当客户端发起 Write Request 时由协议栈调用)
 *
 * @param   connHandle - connection handle (当前连接句柄)
 * @param   pAttr - pointer to attribute being written (被写的属性指针)
 * @param   pValue - data to be written (写入数据)
 * @param   len - length of data (写入数据长度)
 * @param   offset - write offset (0 for normal write) (写偏移,普通写为 0)
 *
 * @return  Success or Failure
 */
static bStatus_t simpleProfile_WriteAttrCB(uint16_t connHandle, gattAttribute_t *pAttr,
                                           uint8_t *pValue, uint16_t len, uint16_t offset, uint8_t method)
{
    bStatus_t status = SUCCESS;
    uint8_t   notifyApp = 0xFF; // 标记需要通知应用层的特征值 ID,0xFF 表示无需通知

    // If attribute permissions require authorization to write, return error
    // 检查属性是否需要授权写入
    if(gattPermitAuthorWrite(pAttr->permissions))
    {
        return (ATT_ERR_INSUFFICIENT_AUTHOR);
    }

    if(pAttr->type.len == ATT_BT_UUID_SIZE)
    {
        // 16-bit UUID 分支处理
        uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]);
        switch(uuid)
        {
            /////////////////////////////////////////////高频操作放最前面(优化分支预测)
            case MuZi_CHAR6_UUID:        // MUZI 命令通道写入(最高频操作)
                // Validate: Make sure it's not a blob operation
                // 校验:不支持长写(Write Long),offset 必须为 0
                if(offset == 0)
                {
                    if(len > MuZi_CHAR6_LEN)
                        status = ATT_ERR_INVALID_VALUE_SIZE; // 写入长度超限
                }
                else
                {
                    status = ATT_ERR_ATTR_NOT_LONG; // 不支持长写操作
                }

                // Write the value (校验通过后拷贝数据到属性缓冲)
                if(status == SUCCESS)
                {
                    tmos_memcpy(pAttr->pValue, pValue, len);
                    notifyApp = MuZi_CHAR6; // 标记需通知应用层处理命令
                }
                break;

            case GATT_CLIENT_CHAR_CFG_UUID:
                // CCCD 写入处理:协议栈自动更新对应的 Config 数组
                // 根据 pAttr 指针自动区分是 CHAR4 还是 CHAR7 的 CCCD
                status = GATTServApp_ProcessCCCWriteReq(connHandle, pAttr, pValue, len,
                                                        offset, GATT_CLIENT_CFG_NOTIFY);
                break;
            //////////////////////////////////////////////////////////////////   
            case SIMPLEPROFILE_CHAR1_UUID:
                if(offset == 0)
                {
                    if(len > SIMPLEPROFILE_CHAR1_LEN)
                        status = ATT_ERR_INVALID_VALUE_SIZE;
                }
                else
                {
                    status = ATT_ERR_ATTR_NOT_LONG;
                }

                if(status == SUCCESS)
                {
                    tmos_memcpy(pAttr->pValue, pValue, len);
                    notifyApp = SIMPLEPROFILE_CHAR1;
                }
                break;

            case SIMPLEPROFILE_CHAR3_UUID:
                if(offset == 0)
                {
                    if(len > SIMPLEPROFILE_CHAR3_LEN)
                        status = ATT_ERR_INVALID_VALUE_SIZE;
                }
                else
                {
                    status = ATT_ERR_ATTR_NOT_LONG;
                }

                if(status == SUCCESS)
                {
                    tmos_memcpy(pAttr->pValue, pValue, len);
                    notifyApp = SIMPLEPROFILE_CHAR3;
                }
                break;

            default:  // 程序不应执行到此处!(特征值 2、4、5、7 没有写权限,协议栈应提前拦截)
                status = ATT_ERR_ATTR_NOT_FOUND;
                break;
        }
    }
    else
    {
        // 128-bit UUID not supported (本服务不支持 128-bit UUID)
        status = ATT_ERR_INVALID_HANDLE;
    }

    // If a characteristic value changed then callback to notify application
    // 如果特征值被成功写入且标记了通知,通过回调通知应用层处理
    if((notifyApp != 0xFF) && simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange)
    {
        simpleProfile_AppCBs->pfnSimpleProfileChange(notifyApp, pValue, len);
    }

    return (status);
}
    

/*********************************************************************
 * @fn          simpleProfile_HandleConnStatusCB (链路连接状态变化回调)
 *              
 * @brief       Simple Profile link status change handler function.
 *              (连接状态变更处理:断开时重置 CCCD,防止重连后误发通知)
 *
 * @param       connHandle - connection handle (连接句柄)
 * @param       changeType - type of change (变更类型:移除/状态标志更新)
 *
 * @return      none
 */
static void simpleProfile_HandleConnStatusCB(uint16_t connHandle, uint8_t changeType)
{
    // Ignore loopback connections (忽略环回测试连接)
    if (connHandle == LOOPBACK_CONNHANDLE)
    {
        return;
    }

    // Check if connection has been removed or transitioned to disconnected state
    // 判断连接是否已被移除或已断开
    uint8_t connRemoved = (changeType == LINKDB_STATUS_UPDATE_REMOVED);
    uint8_t connDown = (changeType == LINKDB_STATUS_UPDATE_STATEFLAGS) && !linkDB_Up(connHandle);

    if (connRemoved || connDown)
    {
        // 断开连接时重置 CCCD,确保下次连接不会自动发送通知
        // 必须由客户端重新写入 CCCD 使能通知后才能发送数据
        GATTServApp_InitCharCfg(connHandle, simpleProfileChar4Config);  // 重置 CHAR4 ACK 通知使能
        GATTServApp_InitCharCfg(connHandle, simpleProfileChar7Config);  // 重置 CHAR7 数据通知使能
    }
}

/*********************************************************************
*********************************************************************/

 

然后就要实现peripheral.h和peripheral.c,这样你的系统就可以跑起来。

以下是peripheral.h文件

/********************************** (C) COPYRIGHT *******************************
 * File Name          : peripheral.h
 * Author             : WCH
 * Version            : V1.0
 * Date               : 2018/12/11
 * Description        :
 *********************************************************************************
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * Attention: This software (modified or not) and binary are used for 
 * microcontroller manufactured by Nanjing Qinheng Microelectronics.
 *******************************************************************************/

#ifndef PERIPHERAL_H
#define PERIPHERAL_H

#ifdef __cplusplus
extern "C" {
#endif

/*********************************************************************
 * INCLUDES
 */

/*********************************************************************
 * CONSTANTS
 */

// Peripheral Task Events 外围角色事件
#define SBP_START_DEVICE_EVT    0x0001  // 启动设备事件
#define SBP_PERIODIC_EVT        0x0002  // 周期性事件
#define SBP_READ_RSSI_EVT       0x0004  // 读取RSSI事件
#define SBP_PARAM_UPDATE_EVT    0x0008  // 参数更新事件

// (0x0010 原 PHY_UPDATE_EVT 已移除,保留占位避免事件位变更)

#define SBP_MUZI_CMD_EVT        0x0020  // MUZI-CMD事件
#define SBP_MUZI_DATA_EVT       0x0040  // MUZI-Data事件
#define SBP_MTU_EXCHANGE_EVT    0x0080  // 从机主动发起 MTU 交换事件

/*********************************************************************
 * MACROS
 */
typedef struct
{                         // 当前连接的连接句柄
    uint16_t connHandle; // Connection handle of current connection
    uint16_t connInterval;
    uint16_t connSlaveLatency;
    uint16_t connTimeout;
} peripheralConnItem_t;

/*********************************************************************
 * FUNCTIONS
 */

/*
 * Task Initialization for the BLE Application
 */
extern void Peripheral_Init(void);

/*
 * Task Event Processor for the BLE Application
 */                                                                        // 单独优化性能O4
extern uint16_t Peripheral_ProcessEvent(uint8_t task_id, uint16_t events)  __attribute__ ((optimize("04")));;

/*********************************************************************
*********************************************************************/

#ifdef __cplusplus
}
#endif

#endif

 

以下是gattprofile.c文件

/********************************** (C) COPYRIGHT *******************************
 * File Name          : peripheral.c
 * Author             : WCH / Modified by Assistant
 * Version            : V1.2
 * Date               : 2023/10/27
 * Description        : Peripheral slave multi-connection application with 
 *                      custom MuZi protocol for ADC data transmission.
 *                      (外设从机多连接应用,集成自定义 MuZi 协议用于 ADC 数据传输)
 *********************************************************************************/

/*********************************************************************
 * INCLUDES (头文件引用)
 */
#include "CONFIG.h"           // 全局配置宏定义
#include "devinfoservice.h"   // 设备信息服务
#include "gattprofile.h"      // GATT 配置文件接口
#include "peripheral.h"       // 外设角色头文件
#include "muzi_crc.h"         // MuZi 协议 CRC-8/MAXIM 校验
#include "muzi_mode.h"        // MuZi 模式管理(快速/普通模式切换)
#include "muzi_random.h"
#include "HAL.h"              // 抽像层接口

// Standard includes for standalone compilation check (标准库,用于独立编译检查)
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>

/*********************************************************************
 * MACROS & CONSTANTS (宏与常量定义)
 */

// How often to perform periodic event 0.625ms x N (周期性事件间隔,单位 0.625ms)
#define SBP_PERIODIC_EVT_PERIOD              1600  // 1600 * 0.625ms = 1s

// [FIX] 命令处理不再使用周期轮询,改为立即触发,此宏保留但不再用于 CMD 事件启动
// #define SBP_MUZI_CMD_EVT_PERIOD             160    // 已废弃: 100ms 轮询延迟是ACK丢失主因

// MUZI-Data event period (MuZi 数据发送事件周期)
#define SBP_MUZI_DATA_EVT_PERIOD            80     // 50ms

// How often to perform read rssi event (RSSI 读取周期)
#define SBP_READ_RSSI_EVT_PERIOD             3200  // 2s

// Parameter update delay (连接参数更新延迟)
#define SBP_PARAM_UPDATE_DELAY               6400  // 4s

// MTU exchange delay (units of 625us, 160 = 100ms) (MTU 交换延迟)
#define SBP_MTU_EXCHANGE_DELAY               160

// Advertising interval (units of 625us, 80=50ms) (广播间隔)
#define DEFAULT_ADVERTISING_INTERVAL         80

// Discoverable mode (可发现模式)
#define DEFAULT_DISCOVERABLE_MODE            GAP_ADTYPE_FLAGS_GENERAL

// Company Identifier: WCH (公司标识符)
#define WCH_COMPANY_ID                       0x07D7

// [FIX] 新增: 延迟 ACK 事件及延迟时间(避开 PHY/参数更新竞争窗口)
#define SBP_MUZI_ACK_DELAY_EVT              0x0800  // 延迟ACK事件位(确保不与已有事件冲突)
#define MUZI_ACK_DELAY_TICKS                32      // 20ms (32 * 0.625ms),等待PHY更新完成

// MuZi CHAR7 Data Fragmentation Constants (MuZi CHAR7 数据分片常量)
#define MuZi_ADC_SOF              0x55       // 帧起始标志
#define MuZi_ADC_MAX_LEN          234        // 最大数据长度,按 3 字节对齐(24-bit 模式)
#define MuZi_CHAR7_OVERHEAD       4          // 协议开销: SOF + FRAG_HDR + LEN + CRC
#define MuZi_MAX_PAYLOAD          (MuZi_CHAR7_LEN - MuZi_CHAR7_OVERHEAD) // 单包最大有效载荷

// MuZi CHAR6 Command Codes (MuZi CHAR6 命令码定义)
#define MuZi_CMD_START_ACQ        0x01       // 开始采集
#define MuZi_CMD_STOP_ACQ         0x02       // 停止采集
#define MuZi_CMD_SET_GAIN         0x03       // 设置增益
#define MuZi_CMD_SET_RATE         0x04       // 设置采样率
#define MuZi_CMD_NEXT_FRAG        0x05       // 请求下一分片(流控)
#define MuZi_CMD_REQ_FRAG         0x06       // 请求指定分片(重传)
#define MuZi_CMD_FAST_MODE        0x07       // 切换到快速模式
#define MuZi_CMD_NORMAL_MODE      0x08       // 切换到普通模式
#define MuZi_CMD_GET_INFO         0x09       // 获取设备信息

// MuZi Acknowledgement Codes (MuZi 应答码)
#define MuZi_ACK_SET_GAIN         0x43       // 增益设置确认
#define MuZi_ACK_SET_RATE         0x44       // 采样率设置确认

// GET_INFO Response Payload Definition (GET_INFO 响应载荷定义)
#define MUZI_INFO_VER_MAJOR       1          // 主版本号
#define MUZI_INFO_VER_MINOR       0          // 次版本号
#define MUZI_INFO_MAX_MTU_LO      LO_UINT16(BLE_BUFF_MAX_LEN) // 最大 MTU 低字节
#define MUZI_INFO_MAX_MTU_HI      HI_UINT16(BLE_BUFF_MAX_LEN) // 最大 MTU 高字节
#define MUZI_FEATURE_FAST_MODE    0x01       // 特性位: 支持快速模式
#define MUZI_FEATURE_24BIT_ADC    0x02       // 特性位: 支持 24-bit ADC
#define MUZI_FEATURE_8BIT_ADC     0x04       // 特性位: 支持 8-bit ADC
#define MUZI_FEATURE_NOTIFY       0x08       // 特性位: 支持通知

/*********************************************************************
 * TYPEDEFS (类型定义)
 */

// ADC Data Sample Format (ADC 数据采样格式枚举)
typedef enum {
    MuZi_ADC_FMT_8BIT  = 1,                 // 8-bit 模式,每样本 1 字节
    MuZi_ADC_FMT_24BIT = 3                   // 24-bit 模式,每样本 3 字节
} MuZi_AdcFmt_t;

// Data Transmission State Machine (数据传输状态机枚举)
typedef enum {
    MuZi_STATE_IDLE = 0,                     // 空闲态
    MuZi_STATE_TX,                           // 传输态
    MuZi_STATE_END                           // 结束态(等待确认)
} MuZi_TxState_t;

// Command Buffer Structure (命令缓冲区结构体,用于异步解耦 BLE 回调与命令解析)
typedef struct {
    uint8_t  data[MuZi_CHAR6_LEN];           // 原始命令数据
    uint16_t len;                            // 数据长度
    _Bool    valid;                          // 有效性标志(CRC 校验通过后置 true)
} MuziCmdBuf_t;

// MuZi Data Transmission Context (MuZi 数据传输上下文,维护传输会话状态)
typedef struct {
    uint8_t        tx_buf[MuZi_ADC_MAX_LEN]; // 待发送 ADC 数据缓冲
    uint16_t       tx_len;                   // 当前数据包总长度
    uint16_t       tx_offset;                // 当前发送偏移量
    uint8_t        pkg_id;                   // 包 ID (0-15 循环)
    uint8_t        frag_idx;                 // 当前分片索引
    uint8_t        frag_total;               // 总分片数
    _Bool          next_requested;           // 主机是否已请求下一片(流控标志)
    MuZi_TxState_t state;                    // 传输状态
    MuZi_AdcFmt_t  fmt;                      // 当前 ADC 格式
    uint8_t        gain;                     // 当前增益值
    uint16_t       rate;                     // 当前采样率
} MuZi_AdcCtx_t;

/*********************************************************************
 * GLOBAL VARIABLES (全局变量)
 */
// Task ID is assigned during initialization (任务 ID,初始化时由 TMOS 分配)
static uint8_t Peripheral_TaskID = INVALID_TASK_ID;

/*********************************************************************
 * LOCAL VARIABLES (局部静态变量)
 */

// Instruction Cache (指令缓存,避免在 BLE 回调中直接处理耗时操作)
static MuziCmdBuf_t g_muziCmdBuf = {0};

// Connection Information (连接信息,记录当前活动连接的参数)
static peripheralConnItem_t peripheralConnList;

// MTU Variable (协商后的 MTU 值)
static uint16_t peripheralMTU = ATT_MTU_SIZE;

// ADC Context (ADC 传输上下文实例)
static MuZi_AdcCtx_t g_muziAdcCtx = {
    .state = MuZi_STATE_IDLE,
    .fmt   = MuZi_ADC_FMT_24BIT,
    .gain  = 1,
    .rate  = 100
};

// [FIX] 新增: 延迟 ACK 缓存(用于模式切换后延迟发送确认)
static uint8_t s_pendingAckCmd = 0;

// GAP - SCAN RSP data (扫描响应数据)
static uint8_t scanRspData[] = {
    0x12, GAP_ADTYPE_LOCAL_NAME_COMPLETE,    // 完整本地名称长度+类型
    'S','i','m','p','l','e',' ','P','e','r','i','p','h','e','r','3','L',
    0x05, GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE, // 从机连接间隔范围
    LO_UINT16(MUZI_MODE_NORMAL_MIN_CONN_INTERVAL),
    HI_UINT16(MUZI_MODE_NORMAL_MIN_CONN_INTERVAL),
    LO_UINT16(MUZI_MODE_NORMAL_MAX_CONN_INTERVAL),
    HI_UINT16(MUZI_MODE_NORMAL_MAX_CONN_INTERVAL),
    0x02, GAP_ADTYPE_POWER_LEVEL,            // 发射功率等级
    0
};

// GAP - Advertisement data (广播数据)
static uint8_t advertData[] = {
    0x02, GAP_ADTYPE_FLAGS,                  // 标志字段
    DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    0x03, GAP_ADTYPE_16BIT_MORE,             // 16-bit UUID (更多服务)
    LO_UINT16(SIMPLEPROFILE_SERV_UUID),
    HI_UINT16(SIMPLEPROFILE_SERV_UUID)
};

// Device Name (设备名称)
static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple Peripheral";

// Performance Optimization: Static buffers for notifications to avoid alloc/free overhead in high speed mode
// (性能优化: 静态通知缓冲区,避免高速模式下频繁 malloc/free 造成的开销和碎片)
static uint8_t s_staticNotifyBuf[MuZi_CHAR7_LEN];
static _Bool   s_isNotifyBusy = false;       // 静态缓冲区忙标志

/*********************************************************************
 * LOCAL FUNCTIONS PROTOTYPES (局部函数原型声明)
 */
static void Peripheral_ProcessTMOSMsg(tmos_event_hdr_t *pMsg);
static void peripheralStateNotificationCB(gapRole_States_t newState, gapRoleEvent_t *pEvent);
static void performPeriodicTask(void);
static void MuZiCmdTask(void);
static void MuZiDataTask(void);
static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len);
static void peripheralParamUpdateCB(uint16_t connHandle, uint16_t connInterval,
                                    uint16_t connSlaveLatency, uint16_t connTimeout);
static void peripheralInitConnItem(peripheralConnItem_t *peripheralConnList);
static void peripheralRssiCB(uint16_t connHandle, int8_t rssi);
static void Peripheral_LinkEstablished(gapRoleEvent_t *pEvent);
static void Peripheral_LinkTerminated(gapRoleEvent_t *pEvent);
static void Peripheral_ProcessGAPMsg(gapRoleEvent_t *pEvent);

// MuZi Specific Functions (MuZi 协议专用函数)
static bStatus_t MuZiSendFragNotify(const uint8_t *frag, uint8_t len);
static void MuZiConnectEventCB(uint32_t timeUs);
static void peripheralChar4Notify(uint8_t *pValue, uint16_t len);
static void MuZiSendAck(uint8_t ack_cmd);
static void MuZiSendErrRsp(uint8_t cmd, uint8_t err_code);
static void MuZiSendRsp(uint8_t cmd, const uint8_t *payload, uint8_t plen);
static uint16_t MuZiLoadAdcData(uint8_t *buf, uint16_t max_len, MuZi_AdcFmt_t fmt);

                                        // 优化级别O4
static uint8_t MuZiPayloadPerFrag(void) __attribute__ ((optimize("04")));;

static bStatus_t MuZiPrepareDataFrag(void);
static bStatus_t MuZiPrepareEndFrag(void);
static void MuZiStartDataTx(void);
static void ParseAndExecute(const uint8_t *buf, uint8_t len);

// Command Handlers (命令处理函数)
static void Cmd_START_ACQ(uint8_t cmd, const uint8_t *payload, uint8_t plen);
static void Cmd_STOP_ACQ(uint8_t cmd, const uint8_t *payload, uint8_t plen);
static void Cmd_SET_GAIN(uint8_t cmd, const uint8_t *payload, uint8_t plen);
static void Cmd_SET_RATE(uint8_t cmd, const uint8_t *payload, uint8_t plen);
static void Cmd_NEXT_FRAG(uint8_t cmd, const uint8_t *payload, uint8_t plen);
static void Cmd_REQ_FRAG(uint8_t cmd, const uint8_t *payload, uint8_t plen);
static void Cmd_FAST_MODE(uint8_t cmd, const uint8_t *payload, uint8_t plen);
static void Cmd_NORMAL_MODE(uint8_t cmd, const uint8_t *payload, uint8_t plen);
static void Cmd_GET_INFO(uint8_t cmd, const uint8_t *payload, uint8_t plen);

/*********************************************************************
 * PROFILE CALLBACKS (协议栈回调函数注册表)
 */

// GAP 角色回调
static gapRolesCBs_t Peripheral_PeripheralCBs = {
    peripheralStateNotificationCB,           // 状态变更通知
    peripheralRssiCB,                        // RSSI 读取完成
    peripheralParamUpdateCB                  // 连接参数更新完成
};

// 广播者回调
static gapRolesBroadcasterCBs_t Broadcaster_BroadcasterCBs = {
    NULL,
    NULL
};

// 绑定管理器回调
static gapBondCBs_t Peripheral_BondMgrCBs = {
    NULL,
    NULL,
    NULL
};

// SimpleProfile 特征值变更回调( 写入回调 )
static simpleProfileCBs_t Peripheral_SimpleProfileCBs = {
    simpleProfileChangeCB
};

/*********************************************************************
 * PUBLIC FUNCTIONS (公开函数)
 */

/*********************************************************************
 * @fn      Peripheral_Init
 * @brief   Initialization function for the Peripheral App Task.
 *          (外设应用任务初始化入口)
 */
void Peripheral_Init()
{
    // 注册任务事件处理函数
    Peripheral_TaskID = TMOS_ProcessEventRegister(Peripheral_ProcessEvent);

    // Initialize MuZi Mode Manager (初始化 MuZi 模式管理器)
    MuZiMode_Init();

    // Setup GAP Peripheral Role Profile (配置 GAP 外设角色)
    {
        uint8_t  initial_advertising_enable = TRUE;
        uint16_t desired_min_interval = MUZI_MODE_NORMAL_MIN_CONN_INTERVAL;
        uint16_t desired_max_interval = MUZI_MODE_NORMAL_MAX_CONN_INTERVAL;

        GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initial_advertising_enable);
        GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);
        GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
        GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desired_min_interval);
        GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desired_max_interval);
    }

    // Set advertising interval (设置广播间隔)
    {
        uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
        GAP_SetParamValue(TGAP_DISC_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_DISC_ADV_INT_MAX, advInt);
        GAP_SetParamValue(TGAP_ADV_SCAN_REQ_NOTIFY, ENABLE); // 使能扫描请求通知
    }

    // Setup GAP Bond Manager (配置绑定管理器)
    {
        uint32_t passkey = 0;
        uint8_t  pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
        uint8_t  mitm = TRUE;                                // 启用中间人保护
        uint8_t  bonding = TRUE;                             // 启用绑定
        uint8_t  ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;        // IO 能力: 仅显示
        
        GAPBondMgr_SetParameter(GAPBOND_PERI_DEFAULT_PASSCODE, sizeof(uint32_t), &passkey);
        GAPBondMgr_SetParameter(GAPBOND_PERI_PAIRING_MODE, sizeof(uint8_t), &pairMode);
        GAPBondMgr_SetParameter(GAPBOND_PERI_MITM_PROTECTION, sizeof(uint8_t), &mitm);
        GAPBondMgr_SetParameter(GAPBOND_PERI_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
        GAPBondMgr_SetParameter(GAPBOND_PERI_BONDING_ENABLED, sizeof(uint8_t), &bonding);
    }

    // Initialize GATT attributes (初始化 GATT 属性表)
    GGS_AddService(GATT_ALL_SERVICES);
    GATTServApp_AddService(GATT_ALL_SERVICES);
    DevInfo_AddService();
    SimpleProfile_AddService(GATT_ALL_SERVICES);

    // Set GAP Characteristics (设置 GAP 特征值)
    GGS_SetParameter(GGS_DEVICE_NAME_ATT, sizeof(attDeviceName), attDeviceName);

    GATT_InitClient();
    
    // Setup SimpleProfile Characteristic Values (初始化 SimpleProfile 特征值默认值)
    {
        uint8_t charValue1[SIMPLEPROFILE_CHAR1_LEN] = {1};
        uint8_t charValue2[SIMPLEPROFILE_CHAR2_LEN] = {2};
        uint8_t charValue3[SIMPLEPROFILE_CHAR3_LEN] = {3};
        uint8_t charValue4[SIMPLEPROFILE_CHAR4_LEN] = {4};
        uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = {1, 2, 3, 4, 5};
        uint8_t charValue6[MuZi_CHAR6_LEN] = {6};
        uint8_t charValue7[MuZi_CHAR7_LEN] = {7};

        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, SIMPLEPROFILE_CHAR1_LEN, charValue1);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, SIMPLEPROFILE_CHAR2_LEN, charValue2);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, SIMPLEPROFILE_CHAR3_LEN, charValue3);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN, charValue4);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, charValue5);
        SimpleProfile_SetParameter(MuZi_CHAR6, MuZi_CHAR6_LEN, charValue6);
        SimpleProfile_SetParameter(MuZi_CHAR7, MuZi_CHAR7_LEN, charValue7);
    }

    // Init Connection Item (初始化连接信息结构体)
    peripheralInitConnItem(&peripheralConnList);

    // Register callbacks (注册各类回调)
    SimpleProfile_RegisterAppCBs(&Peripheral_SimpleProfileCBs);
    GAPRole_BroadcasterSetCB(&Broadcaster_BroadcasterCBs);
    LL_ConnectEventRegister(MuZiConnectEventCB); // 注册底层连接事件回调(用于快速模式)

    // Start device (触发设备启动事件)
    tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT);
}

/*********************************************************************
 * @fn      peripheralInitConnItem
 * @brief   Initialize connection parameters structure.
 *          (初始化连接参数结构体为无效/零值状态)
 */
static void peripheralInitConnItem(peripheralConnItem_t *peripheralConnList)
{
    peripheralConnList->connHandle = GAP_CONNHANDLE_INIT;
    peripheralConnList->connInterval = 0;
    peripheralConnList->connSlaveLatency = 0;
    peripheralConnList->connTimeout = 0;
}

/*********************************************************************
 * @fn      Peripheral_ProcessEvent
 * @brief   Main event processor for the Peripheral task.
 *          (外设任务主事件处理器,TMOS 调度核心)
 */
uint16_t Peripheral_ProcessEvent(uint8_t task_id, uint16_t events)
{
    // 处理系统消息队列
    if(events & SYS_EVENT_MSG)
    {
        uint8_t *pMsg;
        if((pMsg = tmos_msg_receive(Peripheral_TaskID)) != NULL)
        {
            Peripheral_ProcessTMOSMsg((tmos_event_hdr_t *)pMsg);
            tmos_msg_deallocate(pMsg); // 释放消息内存
        }
        return (events ^ SYS_EVENT_MSG);
    }

    // 设备启动事件
    if(events & SBP_START_DEVICE_EVT)
    {
        GAPRole_PeripheralStartDevice(Peripheral_TaskID, &Peripheral_BondMgrCBs, &Peripheral_PeripheralCBs);
        return (events ^ SBP_START_DEVICE_EVT);
    }

    // 周期性任务事件
    if(events & SBP_PERIODIC_EVT)
    {
        if(SBP_PERIODIC_EVT_PERIOD)
        {
            tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);
        }
        performPeriodicTask();
        return (events ^ SBP_PERIODIC_EVT);
    }

    // MuZi 命令处理事件(从缓冲区取出并解析)
    // [FIX] 此事件现在由 simpleProfileChangeCB 立即触发,不再周期轮询
    if(events & SBP_MUZI_CMD_EVT)
    {
        MuZiCmdTask();
        return (events ^ SBP_MUZI_CMD_EVT);
    }

    // [FIX] 新增: 延迟 ACK 事件处理(模式切换后延迟发送确认,避开 PHY 更新窗口)
    if(events & SBP_MUZI_ACK_DELAY_EVT)
    {
        if (s_pendingAckCmd != 0) {
            MuZiSendAck(s_pendingAckCmd);
            s_pendingAckCmd = 0;
        }
        return (events ^ SBP_MUZI_ACK_DELAY_EVT);
    }

    // MuZi 数据发送事件
    if(events & SBP_MUZI_DATA_EVT)
    {
        // In FAST_MODE, data is pushed via ConnectEventCB, so we skip processing here to reduce latency
        // unless we are in normal mode or need to restart a stream
        // (快速模式下数据通过 ConnectEventCB 推送以降低延迟,此处仅处理普通模式)
        if (!MuZiMode_IsFast()) {
            MuZiDataTask();
        }
        return (events ^ SBP_MUZI_DATA_EVT);
    }

    // MTU 交换事件
    if(events & SBP_MTU_EXCHANGE_EVT)
    {
        if(peripheralConnList.connHandle != GAP_CONNHANDLE_INIT)
        {
            attExchangeMTUReq_t req = {0};
            req.clientRxMTU = BLE_BUFF_MAX_LEN;  // MTU = 247
            PRINT("Peripheral request MTU exchange: %d\n", req.clientRxMTU);
            GATT_ExchangeMTU(peripheralConnList.connHandle, &req, Peripheral_TaskID);
        }
        return (events ^ SBP_MTU_EXCHANGE_EVT);
    }

    // 连接参数更新请求事件
    if(events & SBP_PARAM_UPDATE_EVT)
    {
        GAPRole_PeripheralConnParamUpdateReq(peripheralConnList.connHandle,
                                             MUZI_MODE_NORMAL_MIN_CONN_INTERVAL,
                                             MUZI_MODE_NORMAL_MAX_CONN_INTERVAL,
                                             MUZI_MODE_SLAVE_LATENCY,
                                             MUZI_MODE_CONN_TIMEOUT,
                                             Peripheral_TaskID);
        return (events ^ SBP_PARAM_UPDATE_EVT);
    }

    // RSSI 读取事件
    if(events & SBP_READ_RSSI_EVT)
    {
        GAPRole_ReadRssiCmd(peripheralConnList.connHandle);
        tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD);
        return (events ^ SBP_READ_RSSI_EVT);
    }

    return 0;
}

/*********************************************************************
 * @fn      Peripheral_ProcessGAPMsg
 * @brief   Process GAP messages.
 *          (处理 GAP 层消息,如 PHY 更新等)
 */
static void Peripheral_ProcessGAPMsg(gapRoleEvent_t *pEvent)
{
    switch(pEvent->gap.opcode)
    {
        case GAP_SCAN_REQUEST_EVENT:
            break;
        case GAP_PHY_UPDATE_EVENT:
            PRINT("PHY updated: Rx=%d, Tx=%d, status=%d\n",
                  pEvent->linkPhyUpdate.connRxPHYS,
                  pEvent->linkPhyUpdate.connTxPHYS,
                  pEvent->gap.hdr.status);
            break;
        default:
            break;
    }
}

/*********************************************************************
 * @fn      Peripheral_ProcessTMOSMsg
 * @brief   Process TMOS system messages.
 *          (处理 TMOS 系统消息,包括 GAP 和 GATT 消息分发)
 */
static void Peripheral_ProcessTMOSMsg(tmos_event_hdr_t *pMsg)
{
    switch(pMsg->event)
    {
        case GAP_MSG_EVENT:
            Peripheral_ProcessGAPMsg((gapRoleEvent_t *)pMsg);
            break;
        case GATT_MSG_EVENT:
        {
            gattMsgEvent_t *pMsgEvent = (gattMsgEvent_t *)pMsg;
            // 处理 MTU 更新完成事件
            if(pMsgEvent->method == ATT_MTU_UPDATED_EVENT)
            {
                uint16_t negotiatedMTU = pMsgEvent->msg.exchangeMTUReq.clientRxMTU;
                // 钳制 MTU 到合法范围
                if(negotiatedMTU > BLE_BUFF_MAX_LEN) negotiatedMTU = BLE_BUFF_MAX_LEN;
                if(negotiatedMTU < ATT_MTU_SIZE) negotiatedMTU = ATT_MTU_SIZE;
                
                peripheralMTU = negotiatedMTU;
                PRINT("mtu exchange: %d (cap %d)\n", peripheralMTU, BLE_BUFF_MAX_LEN);
            }
            break;
        }
        default:
            break;
    }
}

/*********************************************************************
 * @fn      Peripheral_LinkEstablished
 * @brief   Handle link establishment.
 *          (处理链路建立事件,初始化连接参数并启动各定时任务)
 */
static void Peripheral_LinkEstablished(gapRoleEvent_t *pEvent)
{
    gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *)pEvent;

    // 如果已有连接,断开新连接(单连接限制)
    if(peripheralConnList.connHandle != GAP_CONNHANDLE_INIT)
    {
        GAPRole_TerminateLink(pEvent->linkCmpl.connectionHandle);
        PRINT("Connection max...\n");
    }
    else
    {
        // 保存连接参数
        peripheralConnList.connHandle = event->connectionHandle;
        peripheralConnList.connInterval = event->connInterval;
        peripheralConnList.connSlaveLatency = event->connLatency;
        peripheralConnList.connTimeout = event->connTimeout;
        peripheralMTU = ATT_MTU_SIZE; // 重置 MTU 为默认值

        // [FIX] 移除 CMD 事件的周期启动,命令处理改为立即触发
        // 启动所有周期性任务
        tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD);
        tmos_start_task(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT, SBP_PARAM_UPDATE_DELAY);
        tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD);
        // [FIX] 已删除: tmos_start_task(Peripheral_TaskID, SBP_MUZI_CMD_EVT, SBP_MUZI_CMD_EVT_PERIOD);
        tmos_start_task(Peripheral_TaskID, SBP_MUZI_DATA_EVT, SBP_MUZI_DATA_EVT_PERIOD);
        // 蓝牙芯片从机不主动发更新MTU,让手机APP主机协议为准。
        //tmos_start_task(Peripheral_TaskID, SBP_MTU_EXCHANGE_EVT, SBP_MTU_EXCHANGE_DELAY);

        PRINT("Conn %x - Int %x \n", event->connectionHandle, event->connInterval);
    }
}

/*********************************************************************
 * @fn      Peripheral_LinkTerminated
 * @brief   Handle link termination.
 *          (处理链路断开事件,清理状态并恢复广播)
 */
static void Peripheral_LinkTerminated(gapRoleEvent_t *pEvent)
{
    gapTerminateLinkEvent_t *event = (gapTerminateLinkEvent_t *)pEvent;

    if(event->connectionHandle == peripheralConnList.connHandle)
    {
        // 清空连接信息
        peripheralConnList.connHandle = GAP_CONNHANDLE_INIT;
        peripheralConnList.connInterval = 0;
        peripheralConnList.connSlaveLatency = 0;
        peripheralConnList.connTimeout = 0;
        
        // 停止所有周期性任务
        tmos_stop_task(Peripheral_TaskID, SBP_PERIODIC_EVT);
        tmos_stop_task(Peripheral_TaskID, SBP_MUZI_CMD_EVT);
        tmos_stop_task(Peripheral_TaskID, SBP_MUZI_DATA_EVT);
        tmos_stop_task(Peripheral_TaskID, SBP_READ_RSSI_EVT);
        tmos_stop_task(Peripheral_TaskID, SBP_MTU_EXCHANGE_EVT);
        // [FIX] 停止延迟 ACK 事件并清除缓存
        tmos_stop_task(Peripheral_TaskID, SBP_MUZI_ACK_DELAY_EVT);
        s_pendingAckCmd = 0;

        // 重置 ADC 传输上下文
        g_muziAdcCtx.state = MuZi_STATE_IDLE;
        g_muziAdcCtx.next_requested = false;
        g_muziAdcCtx.tx_len = 0;
        g_muziAdcCtx.tx_offset = 0;
        g_muziAdcCtx.frag_idx = 0;
        g_muziAdcCtx.frag_total = 0;

        // 重置 MuZi 模式
        MuZiMode_Reset();

        // 重新开启广播
        {
            uint8_t advertising_enable = TRUE;
            GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertising_enable);
        }
    }
    else
    {
        PRINT("ERR..\n");
    }
}

/*********************************************************************
 * @fn      peripheralRssiCB
 * @brief   RSSI callback.
 *          (RSSI 读取完成回调)
 */
static void peripheralRssiCB(uint16_t connHandle, int8_t rssi)
{
    PRINT("RSSI -%d dB Conn  %x \n", -rssi, connHandle);
}

/*********************************************************************
 * @fn      peripheralParamUpdateCB
 * @brief   Parameter update complete callback.
 *          (连接参数更新完成回调)
 */
static void peripheralParamUpdateCB(uint16_t connHandle, uint16_t connInterval,
                                    uint16_t connSlaveLatency, uint16_t connTimeout)
{
    if(connHandle == peripheralConnList.connHandle)
    {
        peripheralConnList.connInterval = connInterval;
        peripheralConnList.connSlaveLatency = connSlaveLatency;
        peripheralConnList.connTimeout = connTimeout;
        PRINT("Update %x - Int %x \n", connHandle, connInterval);
    }
    else
    {
        PRINT("ERR..\n");
    }
}

/*********************************************************************
 * @fn      peripheralStateNotificationCB
 * @brief   Notification from the profile of a state change.
 *          (GAP 角色状态变更通知回调,处理设备连接/断开/广播等状态转换)
 */
static void peripheralStateNotificationCB(gapRole_States_t newState, gapRoleEvent_t *pEvent)
{
    switch(newState & GAPROLE_STATE_ADV_MASK)
    {
        case GAPROLE_STARTED:
            PRINT("Initialized..\n");
            break;
        case GAPROLE_ADVERTISING:
            if(pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
            {
                Peripheral_LinkTerminated(pEvent);
                PRINT("Disconnected.. Reason:%x\n", pEvent->linkTerminate.reason);
                PRINT("Advertising..\n");
            }
            else if(pEvent->gap.opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT)
            {
                PRINT("Advertising..\n");
            }
            break;
        case GAPROLE_CONNECTED:
            if(pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
            {
                Peripheral_LinkEstablished(pEvent);
                PRINT("Connected..\n");
            }
            break;
        case GAPROLE_CONNECTED_ADV:
            if(pEvent->gap.opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT)
            {
                PRINT("Connected Advertising..\n");
            }
            break;
        case GAPROLE_WAITING:
            if(pEvent->gap.opcode == GAP_END_DISCOVERABLE_DONE_EVENT)
            {
                PRINT("Waiting for advertising..\n");
            }
            else if(pEvent->gap.opcode == GAP_LINK_TERMINATED_EVENT)
            {
                Peripheral_LinkTerminated(pEvent);
                PRINT("Disconnected.. Reason:%x\n", pEvent->linkTerminate.reason);
            }
            else if(pEvent->gap.opcode == GAP_LINK_ESTABLISHED_EVENT)
            {
                if(pEvent->gap.hdr.status != SUCCESS)
                {
                    PRINT("Waiting for advertising..\n");
                }
                else
                {
                    PRINT("Error..\n");
                }
            }
            break;
        case GAPROLE_ERROR:
            PRINT("Error..\n");
            break;
        default:
            break;
    }
}

/*********************************************************************
 * @fn      performPeriodicTask
 * @brief   Perform periodic application task.
 *          (执行周期性应用任务,非快速模式下通过 CHAR4 发送随机测试数据)
 */
static void performPeriodicTask(void)
{
    if (MuZiMode_IsFast()) return; // 快速模式下跳过此任务

    uint8_t notiData[SIMPLEPROFILE_CHAR4_LEN] = {random_u8()};
    peripheralChar4Notify(notiData, 1);
}

/*********************************************************************
 * @fn      MuZiCmdTask
 * @brief   Process incoming commands from host.
 *          (处理来自主机的命令,从缓冲区拷贝后解析执行,实现回调与处理的解耦)
 */
static void MuZiCmdTask(void)
{
    //HalLedUpdate();   测试LED灯的
    if (g_muziCmdBuf.valid) {
        uint8_t local_buf[MuZi_CHAR6_LEN];
        uint8_t local_len = g_muziCmdBuf.len;
        
        if (local_len > MuZi_CHAR6_LEN) local_len = MuZi_CHAR6_LEN;
        
        // 拷贝到局部变量后立即清除有效标志,防止重复处理
        tmos_memcpy(local_buf, g_muziCmdBuf.data, local_len);
        g_muziCmdBuf.valid = false; 
        
        ParseAndExecute(local_buf, local_len);  // 解析并执行 MuZi 命令帧
    }
}

/*********************************************************************
 * @fn      MuZiDataTask
 * @brief   Process data transmission tasks.
 *          (处理数据传输任务,仅在普通模式且收到主机流控请求时发送下一分片)
 */
static void MuZiDataTask(void)
{
    if (g_muziAdcCtx.state == MuZi_STATE_IDLE) return;
    if (MuZiMode_IsFast()) return; // Handled by ConnectEventCB in fast mode
    if (!g_muziAdcCtx.next_requested) return; // 等待主机 NEXT_FRAG 命令

    g_muziAdcCtx.next_requested = false;

    if (g_muziAdcCtx.state == MuZi_STATE_END) {
        MuZiStartDataTx(); // 上一轮传输结束,开始新一轮
        return;
    }

    MuZiPrepareDataFrag(); // 准备并发送下一个数据分片
}

/*********************************************************************
 * @fn      MuZiConnectEventCB
 * @brief   Connection event callback for FAST_MODE.
 *          Optimized to push data directly during connection events for minimal latency.
 *          (快速模式下的连接事件回调,直接在连接事件中推送数据以实现最低延迟)
 */
static void MuZiConnectEventCB(uint32_t timeUs)
{
    (void)timeUs;

    if (!MuZiMode_IsFast()) return;
    if (peripheralConnList.connHandle == GAP_CONNHANDLE_INIT) return;
    if (g_muziAdcCtx.state == MuZi_STATE_IDLE) return;

    // Try to send as many fragments as possible within this connection event
    // Note: Actual limit depends on BLE stack implementation, usually 1 packet per event per handle
    // But we call Prepare which handles the state machine.
    // (尝试在当前连接事件中尽可能多地发送分片,实际受协议栈限制)
    
    if (g_muziAdcCtx.state == MuZi_STATE_TX) {
        MuZiPrepareDataFrag();
    }
    
    if (g_muziAdcCtx.state == MuZi_STATE_END && g_muziAdcCtx.next_requested)
    {
        g_muziAdcCtx.next_requested = false;
        MuZiStartDataTx();
    }
}

/*********************************************************************
 * @fn      peripheralChar4Notify
 * @brief   Send notification via CHAR4.
 *          (通过 CHAR4 发送通知,带 MTU 检查和动态内存分配)
 */
static void peripheralChar4Notify(uint8_t *pValue, uint16_t len)
{
    if(len > (peripheralMTU - 3))
    {
        PRINT("Too large noti\n");
        return;
    }
    attHandleValueNoti_t noti;
    noti.len = len;
    // 从 GATT 缓冲区管理器分配内存
    noti.pValue = GATT_bm_alloc(peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0);
    if(noti.pValue)
    {
        tmos_memcpy(noti.pValue, pValue, noti.len);
        // [FIX] 捕获发送返回值并打印失败日志,便于排查静默丢包
        bStatus_t status = simpleProfile_Notify(peripheralConnList.connHandle, &noti);
        if(status != SUCCESS)
        {
            PRINT("CHAR4 Noti FAIL: 0x%02X\n", status);
            GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI); // 发送失败则释放
        }
    }
    else
    {
        // [FIX] 新增: 内存分配失败日志
        PRINT("CHAR4 bm_alloc FAIL! len=%d\n", len);
    }
}

/*********************************************************************
 * @fn      simpleProfileChangeCB
 * @brief   Callback when characteristic value changes.
 *          (特征值写入回调,MuZi CHAR6 命令在此接收并进行 CRC 校验)
 */
static void simpleProfileChangeCB(uint8_t paramID, uint8_t *pValue, uint16_t len)
{
    switch(paramID)
    {
        case SIMPLEPROFILE_CHAR1:
        {
            uint8_t newValue[SIMPLEPROFILE_CHAR1_LEN];
            tmos_memcpy(newValue, pValue, len);
            PRINT("profile ChangeCB CHAR1.. \n");
            break;
        }
        case SIMPLEPROFILE_CHAR3:
        {
            uint8_t newValue[SIMPLEPROFILE_CHAR3_LEN];
            tmos_memcpy(newValue, pValue, len);
            PRINT("profile ChangeCB CHAR3..\n");
            break;
        }
        case MuZi_CHAR6: // MuZi 命令通道
        {
            // 基本长度和帧头校验
            if(len > MuZi_CHAR6_LEN || len < 3) return;
            if (pValue[0] != 0xAA) return;

            uint8_t payload_len = pValue[2];
            if (len != (4 + payload_len)) return;

            // 拷贝到命令缓冲区
            tmos_memcpy(g_muziCmdBuf.data, pValue, len);
            g_muziCmdBuf.len = len;
            
            // CRC 校验(使用优化后的单函数版本)
            bool is_valid = CheckCRC(g_muziCmdBuf.data, len);

            if (is_valid) {
                g_muziCmdBuf.valid = true;
                // [FIX] 立即触发命令处理(延迟=0),替代原来的 100ms 周期轮询
                tmos_start_task(Peripheral_TaskID, SBP_MUZI_CMD_EVT, 0);
            }
            else {
                g_muziCmdBuf.valid = false;
                return;
            }
            break;
        }
        default:
            break;
    }
}

/*********************************************************************
 * MuZi Protocol Implementation (MuZi 协议实现)
 *********************************************************************/

/**
 * @brief 发送数据分片通知(带静态缓冲区优化)
 * @param frag 分片数据指针
 * @param len  分片长度
 * @return bStatus_t 发送状态
 */
static bStatus_t MuZiSendFragNotify(const uint8_t *frag, uint8_t len)
{
    if (peripheralConnList.connHandle == GAP_CONNHANDLE_INIT)
        return bleIncorrectMode;
    if (len > (peripheralMTU - 3))
        return bleInvalidRange;

    // Optimization: Use static buffer if not busy, otherwise fall back to alloc
    // This reduces heap fragmentation and allocation time in high-speed modes
    // (优化: 优先使用静态缓冲区以减少堆碎片和分配时间,繁忙时回退到动态分配)
    attHandleValueNoti_t noti;
    noti.len = len;
    
    if (!s_isNotifyBusy) {
        // Use static buffer for zero-latency preparation
        noti.pValue = s_staticNotifyBuf;
        tmos_memcpy(noti.pValue, frag, noti.len);
        s_isNotifyBusy = true;
        
        bStatus_t status = simpleProfile_Notify7(peripheralConnList.connHandle, &noti);
        if (status != SUCCESS) {
            s_isNotifyBusy = false;
        }
        // Note: We do NOT free static buffer. The stack should have copied it or sent it.
        // If the stack requires ownership (async send), this needs to be reverted to bm_alloc.
        // For WCH CH58x/CH59x, simpleProfile_Notify usually copies data internally or sends immediately.
        // If you experience corruption, revert to bm_alloc below.
        // (注意: 静态缓冲区不释放。WCH 协议栈通常会内部拷贝或立即发送。若出现数据损坏请回退到 bm_alloc)
        return status;
    } else {
        // Fallback to dynamic allocation if previous packet is still being processed
        // (回退路径: 上一个包仍在处理中时使用动态分配)
        noti.pValue = GATT_bm_alloc(peripheralConnList.connHandle, ATT_HANDLE_VALUE_NOTI, noti.len, NULL, 0);
        if (noti.pValue == NULL)
            return bleMemAllocError;

        tmos_memcpy(noti.pValue, frag, noti.len);
        bStatus_t status = simpleProfile_Notify7(peripheralConnList.connHandle, &noti);
        if (status != SUCCESS)
            GATT_bm_free((gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI);
        
        return status;
    }
}

/**
 * @brief 发送 MuZi ACK 应答帧
 * @param ack_cmd 应答命令码
 */
static void MuZiSendAck(uint8_t ack_cmd)
{
    uint8_t rsp[4] = {0xAA, ack_cmd, 0x00, 0x00};
    rsp[3] = Crc8Maxim(rsp, 3); // 计算 CRC
    peripheralChar4Notify(rsp, 4);
}

/**
 * @brief 发送 MuZi 错误响应帧
 * @param cmd      原始命令码
 * @param err_code 错误码
 */
static void MuZiSendErrRsp(uint8_t cmd, uint8_t err_code)
{
    uint8_t rsp[6] = {0xAA, 0xFF, 0x02, cmd, err_code, 0x00};
    rsp[5] = Crc8Maxim(rsp, 5);
    peripheralChar4Notify(rsp, 6);
}

/**
 * @brief 发送 MuZi 通用响应帧
 * @param cmd     命令码
 * @param payload 载荷数据
 * @param plen    载荷长度
 */
static void MuZiSendRsp(uint8_t cmd, const uint8_t *payload, uint8_t plen)
{
    uint8_t rsp[32] = {0};
    rsp[0] = 0xAA;
    rsp[1] = cmd;
    rsp[2] = plen;
    if (plen > 0 && payload != NULL) {
        tmos_memcpy(&rsp[3], payload, plen);
    }
    rsp[3 + plen] = Crc8Maxim(rsp, 3 + plen);
    peripheralChar4Notify(rsp, 4 + plen);
}

/**
 * @brief 加载 ADC 模拟数据到缓冲区(测试用)
 * @param buf     目标缓冲区
 * @param max_len 最大长度
 * @param fmt     ADC 格式
 * @return 实际加载的字节数
 */
static uint16_t MuZiLoadAdcData(uint8_t *buf, uint16_t max_len, MuZi_AdcFmt_t fmt)
{
    // 按采样宽度对齐
    uint16_t aligned = (max_len / fmt) * fmt;
    uint16_t len = 0;

    for (uint16_t i = 0; i < aligned; i += fmt) {
        // 生成递增值作为模拟 ADC 数据
        uint32_t val = (uint32_t)(i + (g_muziAdcCtx.pkg_id * 256));
        if (fmt == MuZi_ADC_FMT_24BIT) {
            buf[len++] = val & 0xFF;
            buf[len++] = (val >> 8) & 0xFF;
            buf[len++] = (val >> 16) & 0xFF;
        } else {
            buf[len++] = val & 0xFF;
        }
    }
    return len;
}

/**
 * @brief 计算每个分片的有效载荷大小(根据 MTU 和 ADC 格式动态调整)
 * @return 单分片最大载荷字节数
 */
static inline uint8_t MuZiPayloadPerFrag(void)
{
    // 扣除 ATT 头(3字节)和 MuZi 协议开销
    uint16_t max_payload = (peripheralMTU > MuZi_CHAR7_OVERHEAD + 3)
                           ? (peripheralMTU - MuZi_CHAR7_OVERHEAD - 3)
                           : 0;

    if (max_payload > MuZi_MAX_PAYLOAD)
        max_payload = MuZi_MAX_PAYLOAD;

    // 24-bit 模式需按 3 字节对齐
    if (g_muziAdcCtx.fmt == MuZi_ADC_FMT_24BIT) {
        return (uint8_t)((max_payload / 3) * 3);
    }
    return (uint8_t)max_payload;
}

/**
 * @brief 准备并发送一个数据分片
 * @return bStatus_t 发送状态
 */
static bStatus_t MuZiPrepareDataFrag(void)
{
    static uint8_t frag[MuZi_CHAR7_LEN] = {0};

    if (g_muziAdcCtx.state == MuZi_STATE_IDLE) return SUCCESS;
    if (g_muziAdcCtx.state == MuZi_STATE_END) return bleIncorrectMode;

    uint16_t remaining = g_muziAdcCtx.tx_len - g_muziAdcCtx.tx_offset;
    uint8_t  ppl = MuZiPayloadPerFrag();
    uint8_t  plen = (remaining > ppl) ? ppl : (uint8_t)remaining;

    // 构建分片帧: [SOF][HDR][LEN][PAYLOAD...][CRC]
    frag[0] = MuZi_ADC_SOF;
    frag[1] = ((g_muziAdcCtx.pkg_id & 0x0F) << 4) | (g_muziAdcCtx.frag_idx & 0x0F);
    frag[2] = plen;

    tmos_memcpy(&frag[3], &g_muziAdcCtx.tx_buf[g_muziAdcCtx.tx_offset], plen);
    frag[3 + plen] = Crc8Maxim(frag, 3 + plen);

    // 清零剩余空间(可选,便于调试抓包)
    if ((uint16_t)(4 + plen) < MuZi_CHAR7_LEN)
        tmos_memset(&frag[4 + plen], 0, MuZi_CHAR7_LEN - 4 - plen);

    bStatus_t status = MuZiSendFragNotify(frag, 4 + plen);
    if (status != SUCCESS) return status;

    // 保存当前状态用于失败回滚
    uint16_t prev_offset = g_muziAdcCtx.tx_offset;
    uint8_t  prev_idx    = g_muziAdcCtx.frag_idx;

    // 更新偏移和分片索引
    g_muziAdcCtx.tx_offset += plen;
    g_muziAdcCtx.frag_idx++;

    // 检查是否为最后一个数据分片
    if (g_muziAdcCtx.frag_idx >= g_muziAdcCtx.frag_total)
    {
        if (MuZiPrepareEndFrag() == SUCCESS)
            g_muziAdcCtx.state = MuZi_STATE_END; // 进入结束态等待确认
        else
        {
            // 结束帧发送失败,回滚状态以便重试
            g_muziAdcCtx.tx_offset = prev_offset;
            g_muziAdcCtx.frag_idx  = prev_idx;
        }
    }

    return SUCCESS;
}

/**
 * @brief 准备并发送结束帧(空载荷,仅含 SOF+HDR+CRC)
 * @return bStatus_t 发送状态
 */
static bStatus_t MuZiPrepareEndFrag(void)
{
    static uint8_t end[MuZi_CHAR7_LEN] = {0};
    end[0] = MuZi_ADC_SOF;
    end[1] = (g_muziAdcCtx.pkg_id & 0x0F) << 4; // frag_idx = 0 表示结束帧
    end[2] = 0x00;                               // 长度为 0
    end[3] = Crc8Maxim(end, 3);
    return MuZiSendFragNotify(end, 4);
}

/**
 * @brief 启动新一轮数据传输(加载数据、计算分片数、发送首片)
 */
static void MuZiStartDataTx(void)
{
    // 加载 ADC 数据
    g_muziAdcCtx.tx_len = MuZiLoadAdcData(g_muziAdcCtx.tx_buf,
                                          MuZi_ADC_MAX_LEN,
                                          g_muziAdcCtx.fmt);
    if (g_muziAdcCtx.tx_len == 0) {
        g_muziAdcCtx.state = MuZi_STATE_IDLE;
        return;
    }

    // 计算总分片数
    uint8_t ppl = MuZiPayloadPerFrag();
    g_muziAdcCtx.frag_total = (uint8_t)((g_muziAdcCtx.tx_len + ppl - 1) / ppl);
    if (g_muziAdcCtx.frag_total == 0) g_muziAdcCtx.frag_total = 1;
    // 限制最大分片数为 16(4-bit frag_idx 上限)
    if (g_muziAdcCtx.frag_total > 16) {
        g_muziAdcCtx.frag_total = 16;
        g_muziAdcCtx.tx_len = 16 * ppl;
    }

    // 初始化传输状态
    g_muziAdcCtx.tx_offset = 0;
    g_muziAdcCtx.frag_idx  = 0;
    g_muziAdcCtx.pkg_id    = (g_muziAdcCtx.pkg_id + 1) & 0x0F; // 包 ID 递增
    g_muziAdcCtx.state     = MuZi_STATE_TX;
    g_muziAdcCtx.next_requested = false;

    // 立即发送第一个分片
    MuZiPrepareDataFrag();
}

// ==================== Command Handlers Implementation (命令处理函数实现) ====================

static void Cmd_START_ACQ(uint8_t cmd, const uint8_t *payload, uint8_t plen)
{
    (void)cmd; (void)payload; (void)plen;
    MuZiStartDataTx();
}

static void Cmd_STOP_ACQ(uint8_t cmd, const uint8_t *payload, uint8_t plen)
{
    (void)cmd; (void)payload; (void)plen;
    g_muziAdcCtx.state = MuZi_STATE_IDLE;
}

static void Cmd_SET_GAIN(uint8_t cmd, const uint8_t *payload, uint8_t plen)
{
    (void)cmd; (void)plen;
    g_muziAdcCtx.gain = payload[1];
    MuZiSendAck(MuZi_ACK_SET_GAIN);
}

static void Cmd_SET_RATE(uint8_t cmd, const uint8_t *payload, uint8_t plen)
{
    (void)cmd; (void)plen;
    g_muziAdcCtx.rate = ((uint16_t)payload[0] << 8) | payload[1];
    MuZiSendAck(MuZi_ACK_SET_RATE);
}

static void Cmd_NEXT_FRAG(uint8_t cmd, const uint8_t *payload, uint8_t plen)
{
    (void)payload; (void)plen;
    if (g_muziAdcCtx.state == MuZi_STATE_TX || g_muziAdcCtx.state == MuZi_STATE_END) {
        g_muziAdcCtx.next_requested = true; // 设置流控标志
        // In fast mode, trigger immediate processing if possible
        if (MuZiMode_IsFast()) {
             // The connect event callback will pick this up
             // (快速模式下由 ConnectEventCB 自动拾取)
        } else {
             tmos_set_event(Peripheral_TaskID, SBP_MUZI_DATA_EVT);
        }
    } else {
        MuZiSendErrRsp(cmd, 0x03); // 状态错误
    }
}

static void Cmd_REQ_FRAG(uint8_t cmd, const uint8_t *payload, uint8_t plen)
{
    (void)plen;
    if (g_muziAdcCtx.state != MuZi_STATE_TX) {
        MuZiSendErrRsp(cmd, 0x03);
        return;
    }
    uint8_t req_idx = payload[0] & 0x0F;
    if (req_idx >= g_muziAdcCtx.frag_total) {
        MuZiSendErrRsp(cmd, 0x04); // 索引越界
        return;
    }
    // 跳转到指定分片并重发
    uint8_t ppl = MuZiPayloadPerFrag();
    g_muziAdcCtx.frag_idx = req_idx;
    g_muziAdcCtx.tx_offset = req_idx * ppl;
    MuZiPrepareDataFrag();
}

/**
 * @brief 切换到快速模式
 * [FIX] ACK 延迟发送,避开 MuZiMode_Set 触发的 PHY/参数更新竞争窗口
 */
static void Cmd_FAST_MODE(uint8_t cmd, const uint8_t *payload, uint8_t plen)
{
    (void)payload; (void)plen;
    MuZiModeResult_t r = MuZiMode_Set(MUZI_MODE_FAST, peripheralConnList.connHandle, Peripheral_TaskID);
    switch (r) {
        case MUZI_MODE_RESULT_OK:
            // [FIX] 延迟发送 ACK,等待 PHY 更新完成后再回复
            s_pendingAckCmd = cmd;
            tmos_start_task(Peripheral_TaskID, SBP_MUZI_ACK_DELAY_EVT, MUZI_ACK_DELAY_TICKS);
            break;
        case MUZI_MODE_RESULT_ALREADY:
            MuZiSendAck(cmd); // 无 PHY 变更,可立即回复
            break;
        case MUZI_MODE_RESULT_NO_CONN:
        case MUZI_MODE_RESULT_PHY_FAIL:
            MuZiSendErrRsp(cmd, 0x03);
            break;
    }
}

/**
 * @brief 切换到普通模式
 * [FIX] ACK 延迟发送,避开 MuZiMode_Set 触发的 PHY/参数更新竞争窗口
 */
static void Cmd_NORMAL_MODE(uint8_t cmd, const uint8_t *payload, uint8_t plen)
{
    (void)payload; (void)plen;
    MuZiModeResult_t r = MuZiMode_Set(MUZI_MODE_NORMAL, peripheralConnList.connHandle, Peripheral_TaskID);
    switch (r) {
        case MUZI_MODE_RESULT_OK:
            // [FIX] 延迟发送 ACK,等待 PHY 更新完成后再回复
            s_pendingAckCmd = cmd;
            tmos_start_task(Peripheral_TaskID, SBP_MUZI_ACK_DELAY_EVT, MUZI_ACK_DELAY_TICKS);
            break;
        case MUZI_MODE_RESULT_ALREADY:
            MuZiSendAck(cmd); // 无 PHY 变更,可立即回复
            break;
        case MUZI_MODE_RESULT_NO_CONN:
        case MUZI_MODE_RESULT_PHY_FAIL:
            MuZiSendErrRsp(cmd, 0x03);
            break;
    }
}

static void Cmd_GET_INFO(uint8_t cmd, const uint8_t *payload, uint8_t plen)
{
    (void)payload; (void)plen;
    uint8_t info[6] = {
        MUZI_INFO_VER_MAJOR,
        MUZI_INFO_VER_MINOR,
        MUZI_INFO_MAX_MTU_LO,
        MUZI_INFO_MAX_MTU_HI,
        (MUZI_FEATURE_FAST_MODE | MUZI_FEATURE_24BIT_ADC | MUZI_FEATURE_8BIT_ADC | MUZI_FEATURE_NOTIFY),
        (uint8_t)MuZiMode_Get(),
    };
    MuZiSendRsp(cmd, info, sizeof(info));
}

// ==================== Command Table (命令查找表) ====================

// 命令表条目结构
typedef struct {
    uint8_t             cmd;                 // 命令码
    uint8_t             plen;                // 期望的载荷长度
    void                (*handler)(uint8_t, const uint8_t *, uint8_t); // 处理函数指针
} MuZi_CmdEntry_t;

// 命令映射表(使用表驱动法替代 if-else/switch,易于扩展)
static const MuZi_CmdEntry_t s_muziCmdTable[] = {
    {MuZi_CMD_START_ACQ,  0, Cmd_START_ACQ},
    {MuZi_CMD_STOP_ACQ,   0, Cmd_STOP_ACQ},
    {MuZi_CMD_SET_GAIN,   2, Cmd_SET_GAIN},
    {MuZi_CMD_SET_RATE,   2, Cmd_SET_RATE},
    {MuZi_CMD_NEXT_FRAG,  0, Cmd_NEXT_FRAG},
    {MuZi_CMD_REQ_FRAG,   1, Cmd_REQ_FRAG},
    {MuZi_CMD_FAST_MODE,  0, Cmd_FAST_MODE},
    {MuZi_CMD_NORMAL_MODE,0, Cmd_NORMAL_MODE},
    {MuZi_CMD_GET_INFO,   0, Cmd_GET_INFO},
};

/**
 * @brief 在命令表中查找指定命令码对应的条目
 * @param cmd 命令码
 * @return 匹配条目指针,未找到返回 NULL
 */
static const MuZi_CmdEntry_t *MuZiCmd_Find(uint8_t cmd)
{
    for (uint8_t i = 0; i < (sizeof(s_muziCmdTable) / sizeof(s_muziCmdTable[0])); i++) {
        if (s_muziCmdTable[i].cmd == cmd) return &s_muziCmdTable[i];
    }
    return NULL;
}

/**
 * @brief 解析并执行 MuZi 命令帧
 * @param buf 命令帧数据(已通过 CRC 校验)
 * @param len 帧长度
 */
static void ParseAndExecute(const uint8_t *buf, uint8_t len)
{
    if (len < 4) return;
    uint8_t cmd  = buf[1];
    uint8_t plen = buf[2];

    // 校验帧长度一致性
    if (len != (4 + plen)) {
        MuZiSendErrRsp(cmd, 0x01); // 长度不匹配
        return;
    }

    // 查找命令处理函数
    const MuZi_CmdEntry_t *entry = MuZiCmd_Find(cmd);
    if (entry == NULL) {
        MuZiSendErrRsp(cmd, 0x05); // 未知命令
        return;
    }

    // 校验载荷长度
    if (plen != entry->plen) {
        MuZiSendErrRsp(cmd, 0x02); // 载荷长度错误
        return;
    }

    // 执行命令处理函数
    entry->handler(cmd, &buf[3], plen);
}

四份文件:peripheral.h  peripheral.c  gattprofile.h  gattprofile.c基本是AI生成的,自己修修补补后,经测试稳定性还可以,暂时没有发现问题。与理论值相差有点多,实测如下:

微信图片_20260826140937_68_130

 

    === END ===

 

 

 
posted on 2026-07-13 11:22  木子剑  阅读(10)  评论(0)    收藏  举报