LoRa学习<二>:Rx Duty Cycle模式实验

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

内容:两台设备上电后进入Rx Duty Cycle模式,操作其中一台设备发送数据"LoRa_Test",接收

           到另一台的回复数据“ACK”后,双方都再次进入Rx Duty Cycle模式;

1、SetRxDutyCycle函数参数

2、Rx Duty Cycle工作时序

• The chip is looking for a preamble in either LoRa® or FSK 

芯片在Rx模式监听preamble
• Upon preamble detection, the timeout is stopped and restarted with the value 2 * rxPeriod + sleepPeriod 

在检测到前导码时,超时停止并重新启动Rx模式,超时2*rxPeriod+sleepPeriod,如下图所示

It can be observed that the radio will spend around 1 ms to save the context and go into SLEEP mode and then re-initialize the radio, lock the PLL and go into RX. 

 如下图图所示,Rx模式结束和开始前,各需要500us保存上下文和锁定PLL。

3、Preamble长度设定

The LoRa® packet starts with a preamble sequence which is used to synchronize the receiver with the incoming signal. By default the packet is configured with a 12-symbol long sequence. This is a programmable variable so the preamble length may be extended; for example, in the interest of reducing the receiver duty cycle in receive intensive applications. The 
transmitted preamble length may vary from 10 to 65535 symbols, once the fixed overhead of the preamble data is considered. This permits the transmission of near arbitrarily long preamble sequences.
The receiver undertakes a preamble detection process that periodically restarts. For this reason the preamble length should be configured as identical to the transmitter preamble length. Where the preamble length is not known, or can vary, the maximum preamble length should be programmed on the receiver side.
The preamble is followed by a header which contain information about the following payload. The packet payload is a variable-length field that contains the actual data coded at the error rate either as specified in the header in explicit mode or as selected by the user in implicit mode. An optional CRC may be appended.
Depending upon the chosen mode of operation two types of header are available.

上述深色字体:

        接收器会周期性的重新启动以检测前导码,因此,接收端前导码的长度应该与发射器的长度配置为一致。如果发射端的前导码长度未知或者是可变的,则接收端的前导码长度应当设定为最大值。

  TX端使用长前导码,则必须注意以下公式:即检测到前导码后,启动Rx接收定时器,接收超时时

时间为2*rxPeriod + sleepPeriod.所以必须在这个时间窗口内接收到数据。

4、具体函数

4.1、初始化函数,设定Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);

        假设睡眠时间1000ms,接收时间4ms,Preamble length设定为2000;

        T_{preamble} = (2000 + 4.5) * T_{symbol} = 2004.5 * 0.512us = 1026.304ms

        T_{symbol} = \frac{1}{RS} = \frac{2^{SF}}{BW} = \frac{128}{250} = 0.512ms

#define SUPPORT_RC_MODE
#ifdef SUPPORT_RC_MODE
//#define SUPPORT_CAD_MODE
#endif

#define LORA_BANDWIDTH         1 /* [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] */
#define LORA_SPREADING_FACTOR  7         /* [SF7..SF12] */
#define LORA_CODINGRATE        1         /* [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] */
#define LORA_PREAMBLE_LENGTH   2000      /* Same for Tx and Rx */
#define LORA_SYMBOL_TIMEOUT    5         /* Symbols */
#define LORA_FIX_LENGTH_PAYLOAD_ON                  false
#define LORA_IQ_INVERSION_ON                        false

#define RX_TIMEOUT_VALUE			3000
#define TX_TIMEOUT_VALUE			3000

#define RC_RX_TIIME					256			//4ms
#define RC_SLEEP_TIIME				64000		//1000ms


/* PING string*/
#define TXMSG "LoRa Test"
/* PONG string*/
#define ACKMSG "ACK"
/*Size of the payload to be sent*/
/* Size must be greater of equal the PING and PONG*/
#define MAX_APP_BUFFER_SIZE                            12
#define PAYLOAD_LEN                                    10


static States_t State = RX;
/* App Rx Buffer*/
static uint8_t BufferRx[MAX_APP_BUFFER_SIZE];
/* App Tx Buffer*/
static uint8_t BufferTx[MAX_APP_BUFFER_SIZE];
/* Last  Received Buffer Size*/
uint16_t RxBufferSize = 0;
/* Last  Received packer Rssi*/
int8_t RssiValue = 0;
/* Last  Received packer SNR (in Lora modulation)*/
int8_t SnrValue = 0;



void SubghzApp_Init(void)
{
	/* USER CODE BEGIN SubghzApp_Init_1 */
	APP_LOG(TS_OFF, VLEVEL_M, "\n\rPING PONG\n\r");
	/* 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);
	/* 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;

	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);

	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);

	Radio.SetMaxPayloadLength(MODEM_LORA, 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*/
	Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);
	//Radio.Rx(3000);

	/*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, RxDutyCycleTest_Process);
	/* USER CODE END SubghzApp_Init_2 */
}

不同于static void RadioRx( uint32_t timeout );参数直接代表时间,单位即ms,SetRxDutyCycle

函数两个参数代表的是周期个数,周期时间15.625us。

        如需要接收4ms,睡眠1s,参数如下:
        rxTime = 4ms / 15.625us = 256;

        sleepTime = 1s / 15.625us = 64000;

4.2、RadioSetRxDutyCycle函数原型

static void RadioSetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime )
{
	SUBGRF_SetDioIrqParams( IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_CRC_ERROR | IRQ_HEADER_ERROR | IRQ_RX_DBG | IRQ_PREAMBLE_DETECTED,
							IRQ_RX_DONE | IRQ_RX_TX_TIMEOUT | IRQ_CRC_ERROR | IRQ_HEADER_ERROR | IRQ_RX_DBG | IRQ_PREAMBLE_DETECTED,
							IRQ_RADIO_NONE,
							IRQ_RADIO_NONE );

    /* RF switch configuration */
    SUBGRF_SetSwitch(SubgRf.AntSwitchPaSelect, RFSWITCH_RX);

    SUBGRF_SetRxDutyCycle( rxTime, sleepTime );
}

4.3 按键中断函数,发送数据

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	switch (GPIO_Pin)
	{
		case  BUTTON_SW1_PIN:
			APP_LOG(TS_ON, VLEVEL_L, "But 1\n\r");
			APP_LOG(TS_OFF, VLEVEL_L, "Tx Event\n\r");
			memcpy(BufferTx, TXMSG, sizeof(TXMSG) - 1);
			Radio.Send(BufferTx, PAYLOAD_LEN);
			break;
		
		case  BUTTON_SW2_PIN:
			APP_LOG(TS_ON, VLEVEL_L, "But 2\n\r");
			break;
		
		case  BUTTON_SW3_PIN:
			APP_LOG(TS_ON, VLEVEL_L, "But 3\n\r");
			break;
		
		default:
			break;
	}
}

4.4 Radio状态处理函数

static void RxDutyCycleTest_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)
			{
				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)
				{
					UTIL_TIMER_Stop(&timerLed);
					BSP_LED_On(LED_RED);
					HAL_Delay(Radio.GetWakeupTime() + RX_TIME_MARGIN);
					BSP_LED_Off(LED_RED);

					APP_LOG(TS_OFF, VLEVEL_L, "Tx success, Go Duty Cycle\n\r");
#ifdef SUPPORT_RC_MODE
					Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);
#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\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);
				}
			}
			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;
				APP_LOG(TS_OFF, VLEVEL_L, "Tx Ack Done, Go Duty Cycle\n\r");
#ifdef SUPPORT_RC_MODE
				Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);
#else
				Radio.Rx(3000);
#endif
			}
			break;

		case RX_TIMEOUT:
		case RX_ERROR:
			APP_LOG(TS_OFF, VLEVEL_L, "Rx Timeout, Go Duty Cycle\n\r");
#ifdef SUPPORT_RC_MODE
			Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);
#else
			Radio.Rx(3000);
#endif
			break;
		
		case TX_TIMEOUT:
			APP_LOG(TS_OFF, VLEVEL_L, "Tx Timeout, Go Duty Cycle\n\r");
#ifdef SUPPORT_RC_MODE
			Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);
#else
			Radio.Rx(3000);
#endif
			break;

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

		default:
			break;
	}
}

4.5 测试过程中发现接收不到数据问题:

1、若Tsymbol = 1.024ms,则需要将Duty-Rx的接收时间设定为4m,Preamble length = 5000;

T_{symbol} = \frac{1}{RS} = \frac{2^{SF}}{BW} = \frac{128}{125} =1.024ms

#define RC_RX_TIIME                     128              //2ms
#define RC_SLEEP_TIIME                  320000           //5000ms


#if (( USE_MODEM_LORA == 1 ) && ( USE_MODEM_FSK == 0 ))
#define LORA_BANDWIDTH                              0         /* [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] */
#define LORA_SPREADING_FACTOR                       7         /* [SF7..SF12] */
#define LORA_CODINGRATE                             1         /* [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] */
#define LORA_PREAMBLE_LENGTH                        5000       /* Same for Tx and Rx */
#define LORA_SYMBOL_TIMEOUT                         1         /* Symbols */
#define LORA_FIX_LENGTH_PAYLOAD_ON                  false
#define LORA_IQ_INVERSION_ON                        false

2、若将Duty-Rx的接收时间设定为2ms,则Tsymbol 需= 0.512ms,Preamble length = 10000;

T_{symbol} = \frac{1}{RS} = \frac{2^{SF}}{BW} = \frac{128}{250} = 0.512ms

#define RC_RX_TIIME                     128              //2ms
#define RC_SLEEP_TIIME                  320000           //5000ms


#if (( USE_MODEM_LORA == 1 ) && ( USE_MODEM_FSK == 0 ))
#define LORA_BANDWIDTH                              1         /* [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved] */
#define LORA_SPREADING_FACTOR                       7         /* [SF7..SF12] */
#define LORA_CODINGRATE                             1         /* [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] */
#define LORA_PREAMBLE_LENGTH                        10000       /* Same for Tx and Rx */
#define LORA_SYMBOL_TIMEOUT                         1         /* Symbols */
#define LORA_FIX_LENGTH_PAYLOAD_ON                  false
#define LORA_IQ_INVERSION_ON                        false

个人结论:如果接收时间为2ms,此时设定 Bandwidth = 125K,Tsymbol = 1.024ms,RF在2ms的Rx Time内,不足以完成一次正确的preamble detect即IRQ_PREAMBLE_DETECTED中断不能被触发,固需要将RC_RX_TIME设定4ms。但从功耗方面考虑,使用2ms 接收,则需要设定带宽Bandwidth = 250K; 

4.6、 测试中发现设备进入Rx Duty Cycle之后,总是不定时触发IRQ_PREAMBLE_DETECTED中断,开始以为是干扰导致,固更换频点。但还是低概率出现IRQ = IRQ_PREAMBLE_DETECTED的现象。如下操作后现象消息,即SetRxDutyCycle()前先SetConfig():

	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);
	Radio.SetRxDutyCycle(RC_RX_TIIME, RC_SLEEP_TIIME);

 4.7、实验结果测试,

4.8 测试软件下载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;

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的XML布局,满足您提供的需求: ```xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="基本信息" android:textSize="20sp" android:textStyle="bold" /> <EditText android:id="@+id/edit_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="名字" /> <Spinner android:id="@+id/spinner_group" android:layout_width="match_parent" android:layout_height="wrap_content" android:prompt="@string/group_prompt" /> <EditText android:id="@+id/edit_network" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="网络编号" /> <Spinner android:id="@+id/spinner_lora" android:layout_width="match_parent" android:layout_height="wrap_content" android:entries="@array/lora_versions" android:prompt="@string/lora_prompt" /> <EditText android:id="@+id/edit_serial" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="序号" /> <EditText android:id="@+id/edit_strategy" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="策略名称" /> <EditText android:id="@+id/edit_power" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="功率阈值" /> <Spinner android:id="@+id/spinner_trigger_id" android:layout_width="match_parent" android:layout_height="wrap_content" android:entries="@array/trigger_ids" android:prompt="@string/trigger_id_prompt" /> <EditText android:id="@+id/edit_trigger_time" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="触发时间段" /> <EditText android:id="@+id/edit_trigger_count" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="触发数量" /> </LinearLayout> ``` 请注意,您需要在values文件夹下的strings.xml和arrays.xml文件中定义一些字符串和数组,例如: ```xml <resources> <string name="group_prompt">请选择分组</string> <string name="lora_prompt">请选择Lora协议版本</string> <string-array name="lora_versions"> <item>Lora 1.0</item> <item>Lora 1.1</item> <item>Lora 1.2</item> </string-array> <string name="trigger_id_prompt">请选择触发ID</string> <string-array name="trigger_ids"> <item>1</item> <item>2</item> <item>3</item> <item>4</item> <item>5</item> <item>6</item> <item>7</item> <item>8</item> <item>9</item> <item>10</item> <item>11</item> <item>12</item> <item>13</item> <item>14</item> <item>15</item> <item>16</item> <item>17</item> <item>18</item> <item>19</item> <item>20</item> </string-array> </resources> ``` 希望这可以帮助到您!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值