LoRa学习<三>:CAD模式实验

平台:两台NUCLEO-WL55JC1开发板,即STM32WL55方案,内置LoRa射频SX1261的SOC。

内容:两台设备上电后都启动2s的软定时器,timerout后监听CAD,若有IRQ_CAD_DETECTED中段,则Radio.Rx(3000);   操作其中一台设备按键发送数据"LoRa_CAD",接收段回复“ACK”。然后双方都再次进入CAD监听模式;

一、CAD监听(Channel Activity Detection

       1.1 SX126x之前的产品,只提供对Preamble检测的功能,SX126X系统提供了对CAD检测的功能,芯片设定为进入CAD模式后,SX1261/2将在用户可选择的持续时间内(以数量定义)对频带进行扫描,如果在CAD过程中检测到LoRa®信号,则将返回检测到的通道活动IRQ。

        1.2 不同于Rx Duty Cycle模式,在没有LoRa信号的情况下,MCU需要周期性的设定芯片Start CAD。而Rx Duty Cycle模式整个过程无需MCU参与,直到IRQ_PREAMBLE_DETECTED中断触发MCU。CAD模式工作时序图如下:

        

        1.2 SetCadParams()函数原型

      

         CAD_ONLY:

         芯片在LoRa®中执行CAD操作。一旦做了什么在通道上活动时,芯片返回STBY_RC模式。

         CAD_RX:

         芯片执行CAD操作,如果检测到活动,则它将保持在RX状态,直到检测到数据包或计时器达

         到cadTimeout*15.625us。

         这里再介绍下STBY模式,参考如下

         

         

二、CAD参数设定

        2.1 SX126x数据手册,CAD监听消耗数据如下(SF7,BW125KHz)

        2.2 CAD设定,Best settings更具官方手册设定(BW = 125KHz)

 

        2.3 CAD模式下前导码时长设定,由《LoRa学习<一>:基本参数和数据格式》可知:

                                                T_{symbol} = \frac{2^{SF}}{BW} = 1.024ms 

                                                T_{preamble} = (N_{preamble} + 4.5) * T_{symbol}

              加入设定每隔2s, MCU设定RF执行一次CAD监听,SF=7, BW = 125KHz,根据手册设定

        cadSymbolNum = 2,Cad Time Measure = 2.6641ms。则T_{preamble} > (2s + 2.6641ms),可

        以设定N_{preamble} = 2000,

                            ​​​​T_{preamble} = (2000+ 4.5)* 1.024ms = 2,052.608ms > 2002.6641ms

三、参考代码如下

        3.1 初始化函数

//#define SUPPORT_RX_DUTY_MODE
#define SUPPORT_CAD_MODE

#ifdef SUPPORT_CAD_MODE
#define SUPPORT_CAD_ONLY
#endif

typedef enum
{
	LOWPOWER = 0,
	RX,
	RX_TIMEOUT,
	RX_ERROR,
	TX,
	TX_TIMEOUT,
	PREAMBLE_DETECT,
	START_CAD,
	CAD_DONE,
} States_t;

#ifdef SUPPORT_CAD_MODE
CadRx_t CadRx = CAD_FAIL;
static UTIL_TIMER_Object_t CADTimeoutTimer;
#define CAD_TIMER_TIMEOUT  2000        //Define de CAD timer's timeout here
#endif

#ifdef SUPPORT_CAD_MODE
static 
void OnCadDone( bool channelActivityDetected);
static void CADTimeoutTimeoutIrq(void *context);
#endif


/* Exported functions ---------------------------------------------------------*/
void SubghzApp_Init(void)
{
	/* USER CODE BEGIN SubghzApp_Init_1 */
#ifdef SUPPORT_CAD_MODE
	APP_LOG(TS_OFF, VLEVEL_M, "\n\rCAD Mode Test\n\r");
#elif defined(SUPPORT_RX_DUTY_MODE)
	APP_LOG(TS_OFF, VLEVEL_M, "\n\rRx Duty Cycle Test\n\r");
#endif

	/* Print APP version*/
	APP_LOG(TS_OFF, VLEVEL_M, "APP_VERSION= V%X.%X.%X\r\n",
		(uint8_t)(__APP_VERSION >> __APP_VERSION_MAIN_SHIFT),
		(uint8_t)(__APP_VERSION >> __APP_VERSION_SUB1_SHIFT),
		(uint8_t)(__APP_VERSION >> __APP_VERSION_SUB2_SHIFT));

	/* Led Timers*/
	//UTIL_TIMER_Create(&timerLed, 0xFFFFFFFFU, UTIL_TIMER_ONESHOT, OnledEvent, NULL);
	//UTIL_TIMER_SetPeriod(&timerLed, LED_PERIOD_MS);
	//UTIL_TIMER_Start(&timerLed);
#ifdef SUPPORT_CAD_MODE
	UTIL_TIMER_Create(&CADTimeoutTimer, 0xFFFFFFFFU, UTIL_TIMER_ONESHOT, CADTimeoutTimeoutIrq, NULL);
	UTIL_TIMER_SetPeriod(&CADTimeoutTimer, CAD_TIMER_TIMEOUT);
#endif
	//UTIL_TIMER_Start(&CADTimeoutTimer);
	/* USER CODE END SubghzApp_Init_1 */

	/* Radio initialization */
	RadioEvents.TxDone = OnTxDone;
	RadioEvents.RxDone = OnRxDone;
	RadioEvents.TxTimeout = OnTxTimeout;
	RadioEvents.RxTimeout = OnRxTimeout;
	RadioEvents.RxError = OnRxError;
	RadioEvents.PreambleDetect = PreambleDectect;

#ifdef SUPPORT_CAD_MODE
	RadioEvents.CadDone = OnCadDone;
#endif

	Radio.Init(&RadioEvents);

	/* USER CODE BEGIN SubghzApp_Init_2 */
	/* Radio Set frequency */
	Radio.SetChannel(RF_FREQUENCY);

	/* Radio configuration */
#if ((USE_MODEM_LORA == 1) && (USE_MODEM_FSK == 0))
	APP_LOG(TS_OFF, VLEVEL_M, "---------------\n\r");
	APP_LOG(TS_OFF, VLEVEL_M, "LORA_MODULATION\n\r");
	APP_LOG(TS_OFF, VLEVEL_M, "LORA_BW=%d kHz\n\r", (1 << LORA_BANDWIDTH) * 125);
	APP_LOG(TS_OFF, VLEVEL_M, "LORA_SF=%d\n\r", LORA_SPREADING_FACTOR);

#ifdef SUPPORT_CAD_MODE
	//BW = 0, 125K
	//Tsymbol = 1.024ms
	//Preamble Lenght = 2000;		//2004.5*1.024 = 2,052.608
	Radio.SetTxConfig(MODEM_LORA, TX_OUTPUT_POWER, 0, 0,
		LORA_SPREADING_FACTOR, LORA_CODINGRATE,
		2000, LORA_FIX_LENGTH_PAYLOAD_ON,
		true, 0, 0, LORA_IQ_INVERSION_ON, TX_TIMEOUT_VALUE);

	Radio.SetRxConfig(MODEM_LORA, 0, LORA_SPREADING_FACTOR,
		LORA_CODINGRATE, 0, 2000,
		LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
		0, true, 0, 0, LORA_IQ_INVERSION_ON, true);
#elif defined(SUPPORT_RX_DUTY_MODE)
	//BW = 1, 250K
	//Tsymbol = 0.512ms
	//Preamble Lenght = 2000;		//2004.5*5.012 = 1,026.304
	Radio.SetTxConfig(MODEM_LORA, TX_OUTPUT_POWER, 0, 1,
		LORA_SPREADING_FACTOR, LORA_CODINGRATE,
		2000, LORA_FIX_LENGTH_PAYLOAD_ON,
		true, 0, 0, LORA_IQ_INVERSION_ON, TX_TIMEOUT_VALUE);

	Radio.SetRxConfig(MODEM_LORA, 1, LORA_SPREADING_FACTOR,
		LORA_CODINGRATE, 0, 2000,
		LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
		0, true, 0, 0, LORA_IQ_INVERSION_ON, true);
#else
	Radio.SetTxConfig(MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
		LORA_SPREADING_FACTOR, LORA_CODINGRATE,
		LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
		true, 0, 0, LORA_IQ_INVERSION_ON, TX_TIMEOUT_VALUE);

	Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
		LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
		LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
		0, true, 0, 0, LORA_IQ_INVERSION_ON, true);
#endif


	Radio.SetMaxPayloadLength(MODEM_LORA, MAX_APP_BUFFER_SIZE);

#elif ((USE_MODEM_LORA == 0) && (USE_MODEM_FSK == 1))
	APP_LOG(TS_OFF, VLEVEL_M, "---------------\n\r");
	APP_LOG(TS_OFF, VLEVEL_M, "FSK_MODULATION\n\r");
	APP_LOG(TS_OFF, VLEVEL_M, "FSK_BW=%d Hz\n\r", FSK_BANDWIDTH);
	APP_LOG(TS_OFF, VLEVEL_M, "FSK_DR=%d bits/s\n\r", FSK_DATARATE);

	Radio.SetTxConfig(MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0,
		FSK_DATARATE, 0,
		FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON,
		true, 0, 0, 0, TX_TIMEOUT_VALUE);

	Radio.SetRxConfig(MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE,
		0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH,
		0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, true,
		0, 0, false, true);

	Radio.SetMaxPayloadLength(MODEM_FSK, MAX_APP_BUFFER_SIZE);

#else
	#error "Please define a modulation in the subghz_phy_app.h file."
#endif /* USE_MODEM_LORA | USE_MODEM_FSK */


	/* LED initialization*/
	BSP_LED_Init(LED_GREEN);
	BSP_LED_Init(LED_RED);
	BSP_LED_Init(LED_BLUE);

	BSP_LED_On(LED_GREEN);


	/* KEY initialization*/
	APP_LOG(TS_ON, VLEVEL_L, "Key Init\r\n");
	BSP_PB_Init(BUTTON_SW1, BUTTON_MODE_EXTI);
	BSP_PB_Init(BUTTON_SW2, BUTTON_MODE_EXTI);
	BSP_PB_Init(BUTTON_SW3, BUTTON_MODE_EXTI);

	/*calculate random delay for synchronization*/
	random_delay = (Radio.Random()) >> 22; /*10bits random e.g. from 0 to 1023 ms*/
	/*fills tx buffer*/
	memset(BufferTx, 0x0, MAX_APP_BUFFER_SIZE);

	APP_LOG(TS_ON, VLEVEL_L, "rand=%d\n\r", random_delay);
	/*starts reception*/

#ifdef SUPPORT_RX_DUTY_MODE
	Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);
#elif defined(SUPPORT_CAD_MODE)
	Radio.StartCad();
#else
	Radio.Rx(RX_TIMEOUT_VALUE);
#endif

	/*register task to to be run in while(1) after Radio IT*/
	UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), UTIL_SEQ_RFU, CadTest_Process);
	/* USER CODE END SubghzApp_Init_2 */
}

         3.2 设定CAD,使能CAD_DETECT,设定 CAD_ONLY,即监测到CAD信道信息后,由MCU

切换到Rx模式;

static void RadioStartCad( void )
{
    /* RF switch configuration */
    //SUBGRF_SetSwitch(SubgRf.AntSwitchPaSelect, RFSWITCH_RX);

    SUBGRF_SetDioIrqParams( IRQ_CAD_CLEAR | IRQ_CAD_DETECTED,
                            IRQ_CAD_CLEAR | IRQ_CAD_DETECTED,
                            IRQ_RADIO_NONE,
                            IRQ_RADIO_NONE );

	SUBGRF_SetCadParams(LORA_CAD_02_SYMBOL, 22, 10, LORA_CAD_ONLY, 0); 
	//SUBGRF_SetCadParams(LORA_CAD_02_SYMBOL, 22, 10, LORA_CAD_RX, 192000);

	//SUBGRF_SetCadParams(LORA_CAD_04_SYMBOL, 21, 10, LORA_CAD_RX, 192000);

	SUBGRF_SetCad( );
}

         3.3 CAD Done和CAD Detect中断处理函数,如果是CAD Detect,则进入Rx模式,否则继续

启动Timer,并再次启动CAD Start。


static void RadioIrqProcess( void )
{
    uint8_t size = 0;
    int32_t cfo = 0;

	MW_LOG( TS_OFF, VLEVEL_M,  "IRQ = %d\r\n", SubgRf.RadioIrq);
    switch ( SubgRf.RadioIrq )
    {
        //省略部分
        。。。。。。

	case IRQ_CAD_CLEAR:
		//!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC
		//SUBGRF_SetStandby( STDBY_RC );
		if( ( RadioEvents != NULL ) && ( RadioEvents->CadDone != NULL ) )
		{
		    RadioEvents->CadDone( false );
		}
		break;

	case IRQ_CAD_DETECTED:
		//!< Update operating mode state to a value lower than \ref MODE_STDBY_XOSC
		//SUBGRF_SetStandby( STDBY_RC );
		if( ( RadioEvents != NULL ) && ( RadioEvents->CadDone != NULL ) )
		{
			RadioEvents->CadDone( true );
		}
		break;
		
        。。。。。。
        //省略部分
    default:
        break;
  }
}
#ifdef SUPPORT_CAD_MODE
static void OnCadDone( bool channelActivityDetected)
{
	if( channelActivityDetected == true )
	{
		CadRx = CAD_SUCCESS;
#ifdef SUPPORT_CAD_ONLY
		State = CAD_DONE;
		UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0);
#endif
	}
	else
	{
		CadRx = CAD_FAIL;
		UTIL_TIMER_Start(&CADTimeoutTimer);
	}
}
#endif

         3.4  CAD timer中断函数

#ifdef SUPPORT_CAD_MODE
static void CADTimeoutTimeoutIrq(void *context)
{
	Radio.Standby( );
	State = START_CAD;
	UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0);
}
#endif

         3.5  CadTest_Process状态处理函数


/* USER CODE BEGIN PrFD */
static void CadTest_Process(void)
{
	static bool TxAckFlag = false;
	uint8_t i = 0;

	APP_LOG(TS_OFF, VLEVEL_L, "State=%d\n\r", State);
	switch (State)
	{
		case RX:	
			if (RxBufferSize > 0)										//Received is True;
			{
				APP_LOG(TS_OFF, VLEVEL_L, "Rx:");
				for(i=0; i<RxBufferSize; i++)
				{
					APP_LOG(TS_OFF, VLEVEL_L, "%02x ", BufferRx[i]);
				}
				APP_LOG(TS_OFF, VLEVEL_L, "len(%d)\n\r", RxBufferSize);

				if (strncmp((const char *)BufferRx, ACKMSG, sizeof(ACKMSG) - 1) == 0)
				{
					BSP_LED_On(LED_RED);
					HAL_Delay(Radio.GetWakeupTime() + RX_TIME_MARGIN);
					BSP_LED_Off(LED_RED);

#ifdef SUPPORT_RX_DUTY_MODE
					APP_LOG(TS_OFF, VLEVEL_L, "Rx Ack success, Go Duty Cycle\n\r");
					Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);
#elif defined(SUPPORT_CAD_MODE)
					APP_LOG(TS_OFF, VLEVEL_L, "Rx Ack success, Go CAD Mode\n\r");
					UTIL_TIMER_Start(&CADTimeoutTimer);
#else
					Radio.Rx(3000);
#endif
				}
				else if (strncmp((const char *)BufferRx, TXMSG, sizeof(TXMSG) - 1) == 0)
				{
					BSP_LED_On(LED_GREEN);
					HAL_Delay(Radio.GetWakeupTime() + RX_TIME_MARGIN);
					BSP_LED_Off(LED_GREEN);
					APP_LOG(TS_OFF, VLEVEL_L, "Rx success, Send Ack\n\r");
					memcpy(BufferTx, ACKMSG, sizeof(ACKMSG) - 1);
					Radio.Send(BufferTx, PAYLOAD_LEN);
					TxAckFlag = true;

					//APP_LOG(TS_OFF, VLEVEL_L, "Go Duty Cycle\n\r");
					//Radio.SetRxDutyCycle(1000, 5000);
				}
			}
#ifdef SUPPORT_CAD_MODE
			else													
			{
				if (CadRx == CAD_SUCCESS)
				{
					APP_LOG(TS_ON, VLEVEL_L, "CAD_SUCCESS, Go Rx\n\r");
					Radio.Rx( RX_TIMEOUT_VALUE );
				}
				else
				{
					APP_LOG(TS_ON, VLEVEL_L, "Restart Cad timer\n\r");
					UTIL_TIMER_Start(&CADTimeoutTimer);
				}
			}
#endif
			break;

		case TX:
			if(TxAckFlag == false)
			{
				APP_LOG(TS_OFF, VLEVEL_L, "Tx Msg Done, wait Ack\n\r");
				Radio.Rx(1000);
			}
			else
			{
				TxAckFlag = false;
#ifdef SUPPORT_RX_DUTY_MODE
				APP_LOG(TS_OFF, VLEVEL_L, "Tx Timeout, Go Duty Cycle\n\r");
				Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);
#elif defined(SUPPORT_CAD_MODE)
				UTIL_TIMER_Start(&CADTimeoutTimer);
#else
				Radio.Rx(3000);
#endif
			}
			break;

		case RX_TIMEOUT:
			APP_LOG(TS_OFF, VLEVEL_L, "Rx Timeout, ");
		case RX_ERROR:
#ifdef SUPPORT_RX_DUTY_MODE
			APP_LOG(TS_OFF, VLEVEL_L, "Rx Err, Go Duty Cycle\n\r");
			Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);
#elif defined(SUPPORT_CAD_MODE)
			APP_LOG(TS_OFF, VLEVEL_L, "Rx Err, Go Cad\n\r");
			UTIL_TIMER_Start(&CADTimeoutTimer);
#else
			Radio.Rx(3000);
#endif
			break;
		
		case TX_TIMEOUT:
#ifdef SUPPORT_RX_DUTY_MODE
			APP_LOG(TS_OFF, VLEVEL_L, "Tx Timeout, Go Duty Cycle\n\r");
			Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);
#elif defined(SUPPORT_CAD_MODE)
			APP_LOG(TS_OFF, VLEVEL_L, "Tx Timeout, Go Cad\n\r");
			UTIL_TIMER_Start(&CADTimeoutTimer);
#else
			Radio.Rx(3000);
#endif
			break;

		case PREAMBLE_DETECT:
			APP_LOG(TS_ON, VLEVEL_L, "Preamble Dectect, Go Rx\n\r");
			break;

		case START_CAD:
			Radio.StartCad();
			break;

		case CAD_DONE
:
			APP_LOG(TS_ON, VLEVEL_L, "CAD Dectect, Go Rx\n\r");
			Radio.Rx(3000);
			break;

		case LOWPOWER:
		default:
			break;
	}
}

四、实验结果

 测试软件下载STM32Cube_FW_WL_V1.1.0_Test.rar-嵌入式文档类资源-CSDN文库

      工程路径:STM32Cube_FW_WL_V1.1.0_Test\Projects\NUCLEO-WL55JC\Applications\SubGHz_Phy\SubGHz_Phy_PingPong

       其中Keil MDK和STM32CubeIDE编译测试都有测试OK;

  • 8
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值