15_hopping 跳频通信例程

一、简介

本例程演示 PAN211 芯片的跳频通信功能,包含主动跳频和被动跳频两种模式。主动跳频模式下,发送端和接收端按照预定的跳频序列同步跳频;被动跳频模式下,接收端在超时后自动跳频。

二、条件说明

芯片工作模式:PAN211_CHIPMODE_XN297
CRC:2字节
数据速率:1Mbps
SPI类型:3线SPI
跳频通道:3个通道(20, 40, 65)
数据长度:8字节(7字节应用数据 + 1字节序列号)
工作模式:普通型
发送功率:9dBm
跳频间隔:10ms(主动跳频)/ 30ms(被动跳频)

三、接口(移植)实现

  1. 根据硬件实现以下配置:

  • SPI_CS引脚配置为推挽输出

  • SPI_SCK引脚配置为推挽输出

  • SPI_DATA引脚配置为推挽输出或输入模式,低功耗模式下需配置为输入模式并启用上拉电阻

#define SPI_CS_HIGH      CS_PIN = 1      /* 将SPI_CS引脚设置为高电平 */
#define SPI_CS_LOW       CS_PIN = 0      /* 将SPI_CS引脚设置为低电平 */
#define SPI_SCK_HIGH     SCK_PIN = 1     /* 将SPI_SCK引脚设置为高电平 */
#define SPI_SCK_LOW      SCK_PIN = 0     /* 将SPI_SCK引脚设置为低电平 */
#define SPI_DATA_HIGH    DATA_PIN = 1    /* 将SPI_DATA引脚设置为高电平 */
#define SPI_DATA_LOW     DATA_PIN = 0    /* 将SPI_DATA引脚设置为低电平 */
#define SPI_DATA_STATUS  DATA_PIN        /* 读取SPI_DATA引脚状态 */
#define SPI_DATA_OUTPUT  GPIO_SetModeByPin(P4_3, GPIO_MODE_OUTPUT)  /* 配置SPI_DATA引脚为输出模式 */
#define SPI_DATA_INPUT   GPIO_SetModeByPin(P4_3, GPIO_MODE_INPUT)   /* 配置SPI_DATA引脚为输入模式 */
  1. 根据实际使用的MCU修改SPI接口初始化代码:

/**
 * @brief 初始化PAN211 3线SPI接口
 * @param 无
 * @return 无
 * @note 该函数配置SPI_SCK、SPI_CS和SPI_DATA引脚为GPIO模式,并设置为输出模式
 * @note PAN211没有独立中断引脚,可开启SPI_DATA引脚中断复用功能
 * @note PAN216具有独立中断引脚,可通过P4_5来检测PAN216的中断事件
 */
void BSP_3LineSPIInit(void)
{
    CLK_AHBPeriphClockCmd(CLK_AHBPeriph_GPIO, ENABLE);

    // 将引脚配置为GPIO模式,以软件SPI实现
    SYS_ConfigMFP(P4_0, SYS_MFP_GPIO); // SPI_SCK
    SYS_ConfigMFP(P4_1, SYS_MFP_GPIO); // SPI_CS
    SYS_ConfigMFP(P4_3, SYS_MFP_GPIO); // SPI_DATA
    SYS_ConfigMFP(P4_5, SYS_MFP_GPIO); // IRQ引脚,PAN211未使用

    GPIO_SetModeByPin(P4_1, GPIO_MODE_OUTPUT); // 配置SPI_CS为输出模式
    GPIO_SetModeByPin(P4_0, GPIO_MODE_OUTPUT); // 配置SPI_SCK为输出模式
    GPIO_SetModeByPin(P4_3, GPIO_MODE_OUTPUT); // 配置SPI_DATA为输出模式,后续根据时序需要设置为输入模式
    GPIO_SetModeByPin(P4_5, GPIO_MODE_INPUT);  // PAN211没有中断引脚,PAN216有中断引脚,配置为输入模式

    P41 = 1; // SPI_CS置高,表示未选中
    P40 = 0; // SPI_SCK置低,SPI时钟极性为低电平有效
    P43 = 0; // SPI_DATA置低

    GPIO_EnablePullupPath(P4, BIT3); // 将SPI_DATA引脚设置为输入模式,并启用上拉电阻
}

四、应用范例

1. 主动跳频模式

发送端配置

/* 跳频通道表 */
static const uint8_t HopChannels[] = {20, 40, 65};  /* 3个通道 */
#define HOP_CHANNEL_COUNT (sizeof(HopChannels)/sizeof(HopChannels[0]))

/* 发送计数和通道索引 */
static uint32_t TxCount = 0;
static uint8_t CurrentChannelIndex = 0;

/* 发送数据缓冲区 */
static uint8_t TxBuf[8];  /* 7字节应用数据 + 1字节序列号 */

/* 跳转到下一个通道 */
static void HopToNextChannel(void)
{
    CurrentChannelIndex = (CurrentChannelIndex + 1) % HOP_CHANNEL_COUNT;
    PAN211_SetChannel(HopChannels[CurrentChannelIndex]);
}

int main(void)
{
    /* MCU 初始化 */

    /* 初始化PAN211 */
    if (PAN211_Init() != 1)
    {
        printf("PAN211 init failed.\r\n");
        while (1);
    }

    /* 设置初始通道 */
    PAN211_SetChannel(HopChannels[0]);
    PAN211_ClearIRQFlags(0xFF);

    printf("Active hopping TX demo started on CH%d\r\n", HopChannels[0]);

    while (1)
    {
        test_value++;
        
        /* 准备发送数据 */
        memset(TxBuf, test_value, 7);  /* 7字节应用数据 */
        TxBuf[7] = (TxCount % 3) + 1;  /* 序列号1,2,3循环 */
        
        /* 发送数据 */
        PAN211_WriteFIFO(TxBuf, sizeof(TxBuf));
        PAN211_TxStart();

        /* 等待发送完成 */
        while (!IRQDetected());

        /* 检查发送状态 */
        while (1)
        {
            IRQFlag = PAN211_GetIRQFlags();
            if (IRQFlag & RF_IT_TX_IRQ)
            {
                PAN211_ClearIRQFlags(RF_IT_ALL_IRQ);
                printf("Tx Count:%lu, Channel:%d, Seq:%d\r\n", 
                       ++TxCount, HopChannels[CurrentChannelIndex], TxBuf[7]);
                break;
            }
        }

        /* 发送3个数据包后切换通道 */
        if(TxCount % 3 == 0)
        {
            HopToNextChannel();
        }
        
        /* LED指示 */
        BSP_Led2Toggle();

        /* 延时10ms */
        BSP_DelayMs(10);
    }
}

接收端配置

/* 跳频通道表 */
static const uint8_t HopChannels[] = {20, 40, 65};  /* 3个通道 */
#define HOP_CHANNEL_COUNT (sizeof(HopChannels)/sizeof(HopChannels[0]))
#define LOST_MAX_VALUE    10  /* 最大丢包数 */
#define REV_INTERVAL_NORMAL  10  /* 正常接收间隔 */

/* 接收计数和通道索引 */
static uint32_t RxCount = 0;
static uint8_t CurrentChannelIndex = 0;
static uint8_t LostCount = 0;  /* 丢包计数 */
static uint8_t RevInterval = REV_INTERVAL_NORMAL;  /* 接收间隔 */
static uint8_t HoppingInterval = 10;  /* 跳频间隔 */

/* 接收数据缓冲区 */
static uint8_t RxBuf[8];  /* 7字节应用数据 + 1字节序列号 */

/* 跳转到下一个通道 */
static void HopToNextChannel(void)
{
    CurrentChannelIndex = (CurrentChannelIndex + 1) % HOP_CHANNEL_COUNT;
    PAN211_SetChannel(HopChannels[CurrentChannelIndex]);
}

/* 重置接收状态 */
static void ResetRevStatus(void)
{
    LostCount = 0;
    RevInterval = REV_INTERVAL_NORMAL;
}

int main(void)
{
    /* MCU 初始化 */

    /* 初始化PAN211 */
    if (PAN211_Init() != 1)
    {
        printf("PAN211 init failed.\r\n");
        while (1);
    }

    /* 设置初始通道并启动接收 */
    PAN211_SetChannel(HopChannels[0]);
    PAN211_ClearIRQFlags(0xFF);
    PAN211_RxStart();

    printf("Active hopping RX demo started on CH%d\r\n", HopChannels[0]);

    while (1)
    {
        /* 检查是否收到数据 */
        IRQFlag = PAN211_GetIRQFlags();
        if (IRQFlag & RF_IT_RX_IRQ)
        {
            /* 读取接收到的数据 */
            PAN211_ReadFIFO(RxBuf, 8);
            PAN211_ClearIRQFlags(RF_IT_ALL_IRQ);
            
            /* 根据序列号决定跳频时间 */
            switch(RxBuf[7])  /* 序列号 */
            {
                case 1: HoppingInterval = 25; break;  /* 序列号1,25ms后跳频 */
                case 2: HoppingInterval = 15; break;  /* 序列号2,15ms后跳频 */
                case 3: HoppingInterval = 5; break;   /* 序列号3,5ms后跳频 */
                default: HoppingInterval = 10; break;
            }
            
            printf("Rx Count:%lu, Channel:%d, Seq:%d, Hop in %dms\r\n", 
                   ++RxCount, HopChannels[CurrentChannelIndex], RxBuf[7], HoppingInterval);
            
            /* 重置接收状态 */
            ResetRevStatus();
            timeout_count = 0;  /* 重置超时计数器 */
            
            /* LED指示 */
            BSP_Led2Toggle();
        }
        else
        {
            /* 超时检测 */
            timeout_count++;
            if(timeout_count >= RevInterval)
            {
                timeout_count = 0;
                LostCount++;
                if(LostCount >= LOST_MAX_VALUE)
                {
                    RevInterval = 100;  /* 丢包过多时,切换到100ms超时 */
                }
            }
            
            /* 跳频检测 */
            if(timeout_count >= HoppingInterval)
            {
                timeout_count = 0;
                HopToNextChannel();
                HoppingInterval = 30;  /* 切换频点后默认下一次跳频的时间间隔为30ms */
            }
        }
    }
}

2. 被动跳频模式

接收端配置

/* 跳频通道表 */
static const uint8_t HopChannels[] = {20, 40, 65};  /* 3个通道 */
#define HOP_CHANNEL_COUNT (sizeof(HopChannels)/sizeof(HopChannels[0]))

/* 接收计数和通道索引 */
static uint32_t RxCount = 0;
static uint8_t CurrentChannelIndex = 0;

/* 接收数据缓冲区 */
static uint8_t RxBuf[8];  /* 7字节应用数据 + 1字节序列号 */

/* 跳转到下一个通道 */
static void HopToNextChannel(void)
{
    CurrentChannelIndex = (CurrentChannelIndex + 1) % HOP_CHANNEL_COUNT;
    PAN211_SetChannel(HopChannels[CurrentChannelIndex]);
}

int main(void)
{
    /* MCU 初始化 */

    /* 初始化PAN211 */
    if (PAN211_Init() != 1)
    {
        printf("PAN211 init failed.\r\n");
        while (1);
    }

    /* 设置初始通道并启动接收 */
    PAN211_SetChannel(HopChannels[0]);
    PAN211_ClearIRQFlags(0xFF);
    PAN211_RxStart();

    printf("Passive hopping RX demo started on CH%d\r\n", HopChannels[0]);
    while (1)
    {
        if (IRQDetected())  /* 如果引脚上有中断事件 */
        {
            delay_count = 0;
            while(1)
            {
                IRQFlag = PAN211_GetIRQFlags(); /* 获取中断标志 */
                if (IRQFlag & RF_IT_RX_IRQ)     /* 接收中断 */
                {            
                    PAN211_ReadFIFO(RxBuf, sizeof(RxBuf)); /* 读取FIFO数据到RxBuf缓冲区 */
                    PAN211_WriteFIFO(RxBuf, sizeof(RxBuf));
                    PAN211_ClearIRQFlags(RF_IT_RX_IRQ); /* 清除接收中断标志 */
                    printf("Rx Count:%lu, Channel:%d\r\n", 
                           ++RxCount, HopChannels[CurrentChannelIndex]);
                    printf("+RxHexData:\r\n");
                    PrintHex(RxBuf, sizeof(RxBuf)); /* 以十六进制方式打印接收数据 */
                    BSP_Led3Toggle(); /* 切换LED3状态 */
                }
                else if (IRQFlag & RF_IT_TX_IRQ) /* 发送中断 */
                {
                    PAN211_ClearIRQFlags(RF_IT_TX_IRQ); /* 清除发送中断标志 */
                    printf(">> RF_IT_TX_IRQ[0x%02X].\r\n", IRQFlag);
                    BSP_Led2Toggle(); /* 切换LED2状态 */
                    break;
                }
                else if (IRQFlag & RF_IT_PID_ERR_IRQ) /* PID错误中断 */
                {
                    PAN211_ClearIRQFlags(RF_IT_PID_ERR_IRQ); /* 清除PID错误中断标志 */
                    printf(">> RF_IT_PID_ERR_IRQ[0x%02X].\r\n", IRQFlag);
                    break;  
                }
                else if (IRQFlag & RF_IT_CRC_ERR_IRQ) /* CRC错误中断 */
                {
                    PAN211_ClearIRQFlags(RF_IT_CRC_ERR_IRQ); /* 清除CRC错误中断标志 */
                    printf(">> RF_IT_CRC_ERR_IRQ[0x%02X]. \r\n", IRQFlag);
                    break;
                } 
            }
        }
        else
        {
            /* 超时检测 */
            delay_count++;
            if(delay_count >= 30)  /* 30ms超时 */
            {
                delay_count = 0;
                HopToNextChannel();
            }
            BSP_DelayMs(1);
        }
    }
}

五、参考文档

03_DOC/PAN211x 跳频参考.pdf