【WCH蓝牙系列芯片】-基于CH592开发板—2.4G模拟BLE广播发送

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

  在CH592程序中,包含RF_PHY:2.4G非标准无线收发例程,2.4G模拟BLE广播发送,通过在2.4GHz频段实现BLE广播的等效传输,这种技术可以不直接利用BLE协议栈,BLE协议栈的资源占用较大,而是用2.4G的方式实现BLE蓝牙广播(非协议栈实现BLE蓝牙广播),这边比较适合于轻量化的应用,比如beacon应用等。

  在RF_PHY程序的初始化中,需要依据BLE的核心规范,将接入地址设为蓝牙包的接入地址0x8e89bed6,rf_Config.accessAddress = 0x8E89BED6;

void RF_Init(void)
{
    uint8_t    state;
    rfConfig_t rf_Config;
    tmos_memset(&rf_Config, 0, sizeof(rfConfig_t));
    taskID = TMOS_ProcessEventRegister(RF_ProcessEvent);
    //依据BLE的核心规范,填写Access Address 和 CRC24 的入值.
    rf_Config.accessAddress = 0x8E89BED6;  // BLE广播的固定访问地址 // 禁止使用0x55555555以及0xAAAAAAAA ( 建议不超过24次位反转,且不超过连续的6个0或1 )
    rf_Config.CRCInit = 0x555555;  // BLE CRC校验的初始值
    rf_Config.Channel = 37;   // 配置射频信道为BLE广播信道37
//    rf_Config.Frequency = 2480000;
#if(RF_AUTO_MODE_EXAM)
    rf_Config.LLEMode = LLE_MODE_AUTO;
#else
//    rf_Config.LLEMode = LLE_MODE_BASIC | LLE_MODE_EX_CHANNEL; // 使能 LLE_MODE_EX_CHANNEL 表示 选择 rf_Config.Frequency 作为通信频点
    rf_Config.LLEMode = LLE_MODE_BASIC;   //基础模式
#endif
    rf_Config.rfStatusCB = RF_2G4StatusCallBack;
    rf_Config.RxMaxlen = 251;    //最大接收长度为251字节
    state = RF_Config(&rf_Config);
    PRINT("rf 2.4g init: %x\n", state);
    uint8_t mac_addr[8];
    GetMACAddress(mac_addr);  // 获取MAC地址
    tmos_memcpy(&TX_DATA[0], mac_addr, 6);  //获取MAC地址并复制到TX_DATA
    hex_dump(mac_addr, 6);  //十六进制打印MAC地址

//    { // RX mode
//#if(RF_AUTO_MODE_EXAM)
//        rx_end_flag = FALSE;
//#endif
//        state = RF_Rx(TX_DATA, 10, 0xFF, 0xFF);
//        PRINT("RX mode.state = %x\n", state);
//    }

    { // TX mode
        tmos_set_event( taskID , SBP_RF_PERIODIC_EVT );
    }
}

 

  将配置射频信道为BLE广播信道37,38,39,三个通道中其中一个, rf_Config.Channel = 37;

  在初始化中,并将设备的内部的MAC地址打印出来,然后开启一个周期性的TMOS事件任务。

image

  在TMOS任务中,通过RF_Shut(); 先关闭射频模块:在切换射频状态前先关闭当前射频操作,避免冲突,然后再调用ble_Broadcast_tx()函数,模拟BLE的广播帧,发送BLE广播包,执行BLE广播发送操作。

image

  构造BLE的广播测试数据,按照广播的内容格式,一包广播数据最大是31个字节,不能超过31个字节就可以

  0x99,0x88,0x77, 0xE4, 0xC2, 0x84,——————设置的广播的MAC地址

  9,0x09,'B','L','E','-','T','E','S','T',————设置蓝牙的广播名字(长度+类型+数据。其中长度是包含数据+类型(一个字节长度))

  8,0xFF,0x11,0x22,0x33,0x44,0x55,0x66,0x77 ————设置的厂商自定义数据

image

  然后通过调用RF_TX接口函数,将分别控制发送数据、长度、发送类型和接收类型,

  RF_Tx( ble_adv_test_data,sizeof(ble_adv_test_data), 0x02, 0xFF );这里设置发送类型为0X02,是非连接广播,接收类型设置为0xff, 接收所有 “匹配类型” 的应答包。

  通过BLE调试工具软件,可以观察到2.4G模拟BLE发送的蓝牙广播包数据

image

image

 

 附件程序:

#include "CONFIG.h"
#include "RF_PHY.h"

extern uint8_t ble_Broadcast_tx(void);  // 外部声明BLE广播发送函数

//将传入的字节数组以十六进制格式打印出来
void hex_dump(uint8_t* data,uint32_t length)
{
    for(uint32_t i=0;i<length;i++)
    {
        PRINT("%02x ",*data);
        data++;
    }
    PRINT("\r\n");
}

/*********************************************************************
 * GLOBAL TYPEDEFS
 */
#define RF_AUTO_MODE_EXAM       0  // 射频模式选择:0=手动模式,1=自动模式(发送后自动等待接收应答)

uint8_t taskID;  // 任务ID,用于TMOS操作系统的事件管理
uint8_t TX_DATA[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};   // 默认发送数据缓冲区

//ble adv data for RF-PHY test  // BLE广播测试数据(用于RF-PHY层测试)
static uint8_t ble_adv_test_data[] =
{
                                0x99,0x88,0x77, 0xE4, 0xC2, 0x84,        //MAC ADDR
                                9,0x09,'B','L','E','-','T','E','S','T',  //ADV name
                                8,0xFF,0x11,0x22,0x33,0x44,0x55,0x66,0x77  //  Manufacturer data
};

volatile uint8_t tx_end_flag=0;  // 发送完成标志
volatile uint8_t rx_end_flag=0;   // 接收完成标志
/*********************************************************************
 * @fn      RF_Wait_Tx_End
 *
 * @brief   手动模式等待发送完成,自动模式等待发送-接收完成,必须在RAM中等待,等待时可以执行用户代码,但需要注意执行的代码必须运行在RAM中,否则影响发送
 *
 * @return  none
 */
__HIGH_CODE  // 声明函数在高地址RAM中执行(确保快速访问,不被Flash读取延迟影响)
__attribute__((noinline))  // 禁止编译器内联(保证函数独立存在于RAM)
void RF_Wait_Tx_End()
{
    uint32_t i=0;
    while(!tx_end_flag)     // 循环等待发送完成标志置位
    {
        i++;
        __nop();  // 空操作(延迟)
        __nop();
        // 约5ms超时保护(防止无限等待,FREQ_SYS为系统时钟频率)
        if(i>(FREQ_SYS/1000))  // FREQ_SYS/1000 ≈ 5ms对应的时钟周期数
        {
            tx_end_flag = TRUE;  // 超时后强制置位标志
        }
    }
}
/*********************************************************************
 * @fn      RF_Wait_Rx_End
 *
 * @brief   自动模式等待应答发送完成,必须在RAM中等待,等待时可以执行用户代码,但需要注意执行的代码必须运行在RAM中,否则影响发送
 *
 * @return  none
 */
__HIGH_CODE
__attribute__((noinline))
void RF_Wait_Rx_End()
{
    uint32_t i=0;
    while(!rx_end_flag)   // 循环等待接收完成标志置位
    {
        i++;
        __nop();
        __nop();
        // 约5ms超时
        if(i>(FREQ_SYS/1000))
        {
            rx_end_flag = TRUE;  // 超时后强制置位标志
        }
    }
}
/*********************************************************************
 * @fn      RF_2G4StatusCallBack
 *
 * @brief   RF 状态回调,此函数在中断中调用。注意:不可在此函数中直接调用RF接收或者发送API,需要使用事件的方式调用
 *          在此回调中直接使用或调用函数涉及到的变量需注意,此函数在中断中调用。
 *
 * @param   sta     - 状态类型
 * @param   crc     - crc校验结果
 * @param   rxBuf   - 数据buf指针
 *
 * @return  none
 */
void RF_2G4StatusCallBack(uint8_t sta, uint8_t crc, uint8_t *rxBuf)
{
    switch(sta)
        {
            case TX_MODE_TX_FINISH:  // 发送模式:发送完成
            {
    #if(!RF_AUTO_MODE_EXAM)  // 手动模式下:设置发送完成标志
                tx_end_flag = TRUE;
    #endif
                break;
            }
            case TX_MODE_TX_FAIL:  // 发送模式:发送失败
            {
                tx_end_flag = TRUE;  // 无论模式,发送失败均置位标志
                break;
            }
            case TX_MODE_RX_DATA:  // 发送模式:接收到应答数据(自动模式)
            {
    #if(RF_AUTO_MODE_EXAM)  // 自动模式下处理
                tx_end_flag = TRUE;  // 置位发送完成标志
                if (crc == 0) {  // CRC校验通过
                    uint8_t i;

                    PRINT("tx recv,rssi:%d\n", (int8_t)rxBuf[0]);  // 打印接收信号强度(RSSI)
                    PRINT("len:%d-", rxBuf[1]);  // 打印数据长度

                    for (i = 0; i < rxBuf[1]; i++) {  // 打印接收数据
                        PRINT("%x ", rxBuf[i + 2]);
                    }
                    PRINT("\n");
                } else {  // CRC校验失败
                    if (crc & (1<<0)) {
                        PRINT("crc error\n");  // CRC错误
                    }
                    if (crc & (1<<1)) {
                        PRINT("match type error\n");  // 数据类型匹配错误
                    }
                }
    #endif
                break;
            }
            case TX_MODE_RX_TIMEOUT:  // 发送模式:接收应答超时(约200us)
            {
    #if(RF_AUTO_MODE_EXAM)  // 自动模式下置位发送完成标志
                tx_end_flag = TRUE;
    #endif
                break;
            }
            case RX_MODE_RX_DATA:  // 接收模式:接收到数据
            {
                if (crc == 0) {  // CRC校验通过
                    uint8_t i;
    #if(RF_AUTO_MODE_EXAM)
                    RF_Wait_Rx_End();  // 自动模式下等待接收完成
    #endif
                    PRINT("rx recv, rssi: %d\n", (int8_t)rxBuf[0]);  // 打印RSSI
                    PRINT("len:%d-", rxBuf[1]);  // 打印数据长度

                    for (i = 0; i < rxBuf[1]; i++) {  // 打印接收数据
                        PRINT("%x ", rxBuf[i + 2]);
                    }
                    PRINT("\n");
                } else {  // 校验失败
                    if (crc & (1<<0)) {
                        PRINT("crc error\n");
                    }
                    if (crc & (1<<1)) {
                        PRINT("match type error\n");
                    }
                }
    #if(!RF_AUTO_MODE_EXAM)  // 手动模式下:触发接收事件(在事件处理函数中处理)
                tmos_set_event(taskID, SBP_RF_RF_RX_EVT);
    #endif
                break;
            }
            case RX_MODE_TX_FINISH:  // 接收模式:应答发送完成
            {
    #if(RF_AUTO_MODE_EXAM)  // 自动模式下:置位接收完成标志并触发事件
                rx_end_flag = TRUE;
                tmos_set_event(taskID, SBP_RF_RF_RX_EVT);
    #endif
                break;
            }
            case RX_MODE_TX_FAIL:  // 接收模式:应答发送失败
            {
    #if(RF_AUTO_MODE_EXAM)  // 自动模式下:置位接收完成标志并触发事件
                rx_end_flag = TRUE;
                tmos_set_event(taskID, SBP_RF_RF_RX_EVT);
    #endif
                break;
            }
        }
    }
/*********************************************************************
 * @fn      RF_ProcessEvent
 *
 * @brief   RF 事件处理
 *
 * @param   task_id - 任务ID
 * @param   events  - 事件标志
 *
 * @return  未完成事件
 */

// 射频事件处理函数,用于处理与射频相关的各类事件
uint16_t RF_ProcessEvent(uint8_t task_id, uint16_t events)
{
    // 处理系统消息事件(SYS_EVENT_MSG):系统级别的消息通知
    if(events & SYS_EVENT_MSG)
    {
        uint8_t *pMsg;  // 消息指针,用于接收系统消息

        // 接收当前任务的消息:tmos_msg_receive从消息队列中获取指定任务的消息
        if((pMsg = tmos_msg_receive(task_id)) != NULL)
        {

            tmos_msg_deallocate(pMsg);  // 释放消息内存:处理完消息后必须释放,避免内存泄漏
        }

        return (events ^ SYS_EVENT_MSG);  // 返回未处理的事件:清除已处理的SYS_EVENT_MSG,保留其他事件
    }

    // 处理设备启动事件(SBP_RF_START_DEVICE_EVT):设备初始化完成后触发
    if(events & SBP_RF_START_DEVICE_EVT)
    {

        tmos_start_task(taskID, SBP_RF_PERIODIC_EVT, 1000);   // 启动周期性任务:在当前任务(taskID)中注册SBP_RF_PERIODIC_EVT事件,延迟1000个时间单位(通常为ms)后触发

        return events ^ SBP_RF_START_DEVICE_EVT;  // 清除已处理的启动事件,返回剩余事件
    }

    // 处理周期性事件(SBP_RF_PERIODIC_EVT):按固定周期执行的射频操作
    if(events & SBP_RF_PERIODIC_EVT)
    {
        RF_Shut();  // 关闭射频模块:在切换射频状态前先关闭当前射频操作,避免冲突
        tx_end_flag = FALSE;    // 重置发送结束标志:标记发送过程未完成
        if(!RF_Tx( ble_adv_test_data,sizeof(ble_adv_test_data), 0x02, 0xFF ))  // 发送数据:通过RF_Tx函数发送ble_adv_test_data缓冲区,后两个参数可能为目标地址(0x02通常(非连接广播))
        {

            RF_Wait_Tx_End();  // 等待发送完成:如果发送函数需要阻塞等待,调用此函数确保数据发送完毕
        }
        ble_Broadcast_tx();  // 发送BLE广播包:执行BLE广播发送操作,可能是模拟BLE的广播帧
        tmos_start_task(taskID, SBP_RF_PERIODIC_EVT, 1000); // 重新启动周期性任务:设置下一次周期性事件在1000个时间单位后触发,形成循环
        return events ^ SBP_RF_PERIODIC_EVT;  // 清除已处理的周期性事件,返回剩余事件
    }

    // 处理射频接收事件(SBP_RF_RF_RX_EVT):接收到数据时触发
    if(events & SBP_RF_RF_RX_EVT)
    {
        uint8_t state;  // 接收状态变量,用于存储RF_Rx的返回状态
        RF_Shut();  // 关闭射频模块:在切换到接收模式前关闭当前操作
        TX_DATA[0]++;  // 更新发送缓冲区首字节:可能用于标记接收计数或状态(示例中简单递增)

        // 如果开启自动模式示例(RF_AUTO_MODE_EXAM宏定义),则重置接收结束标志
#if(RF_AUTO_MODE_EXAM)
        rx_end_flag = FALSE;
#endif
        state = RF_Rx(TX_DATA, 10, 0xFF, 0xFF);    // 进入接收模式:通过RF_Rx函数接收数据到TX_DATA缓冲区(最多10字节),后两个参数可能为接收地址过滤
        PRINT("RX mode.state = %x\n", state);  // 打印接收状态:调试用,输出当前接收模式的状态值
        return events ^ SBP_RF_RF_RX_EVT;    // 清除已处理的接收事件,返回剩余事件
    }

    // 所有事件均已处理,返回0
    return 0;
}

/*********************************************************************
 * @fn      RF_Init
 *
 * @brief   RF 初始化
 *
 * @return  none
 */
void RF_Init(void)
{
    uint8_t    state;
    rfConfig_t rf_Config;

    tmos_memset(&rf_Config, 0, sizeof(rfConfig_t));
    taskID = TMOS_ProcessEventRegister(RF_ProcessEvent);

    //依据BLE的核心规范,填写Access Address 和 CRC24 的入值.
    rf_Config.accessAddress = 0x8E89BED6;  // BLE广播的固定访问地址 // 禁止使用0x55555555以及0xAAAAAAAA ( 建议不超过24次位反转,且不超过连续的6个0或1 )
    rf_Config.CRCInit = 0x555555;  // BLE CRC校验的初始值
    rf_Config.Channel = 37;   // 配置射频信道为BLE广播信道37
//    rf_Config.Frequency = 2480000;
#if(RF_AUTO_MODE_EXAM)
    rf_Config.LLEMode = LLE_MODE_AUTO;
#else
//    rf_Config.LLEMode = LLE_MODE_BASIC | LLE_MODE_EX_CHANNEL; // 使能 LLE_MODE_EX_CHANNEL 表示 选择 rf_Config.Frequency 作为通信频点
    rf_Config.LLEMode = LLE_MODE_BASIC;   //基础模式
#endif
    rf_Config.rfStatusCB = RF_2G4StatusCallBack;
    rf_Config.RxMaxlen = 251;    //最大接收长度为251字节
    state = RF_Config(&rf_Config);
    PRINT("rf 2.4g init: %x\n", state);

    uint8_t mac_addr[8];
    GetMACAddress(mac_addr);  // 获取MAC地址
    tmos_memcpy(&TX_DATA[0], mac_addr, 6);  //获取MAC地址并复制到TX_DATA

    hex_dump(mac_addr, 6);  //十六进制打印MAC地址

//    { // RX mode
//#if(RF_AUTO_MODE_EXAM)
//        rx_end_flag = FALSE;
//#endif
//        state = RF_Rx(TX_DATA, 10, 0xFF, 0xFF);
//        PRINT("RX mode.state = %x\n", state);
//    }

    { // TX mode
        tmos_set_event( taskID , SBP_RF_PERIODIC_EVT );  // 初始触发周期性事件
    }
}

// BLE广播发送函数
uint8_t ble_Broadcast_tx(void)
{
   RF_Shut( );  // 关闭当前射频操作(确保状态干净)

   //tx type :0X02 for no connected adv   // 发送BLE广播测试数据:类型0x02(非连接广播),目标地址0xFF(广播)
   return RF_Tx( ble_adv_test_data,sizeof(ble_adv_test_data), 0x02, 0xFF );
}

 

 

 

 

posted on 2025-08-29 18:46  凡仕  阅读(168)  评论(0)    收藏  举报