LoRa入门(2)—— PingPong测试(软件篇)
上节讲述了,PingPong测试的STM32CubeMX初始化流程,这节讲解代码生成后,需要修改的板级驱动和LoRa的发送接收函数。
(参考:《Lora入门(1)—— PingPong测试(CubeMX篇)》)
STM32CubeMX生成代码后,在radio_board_if.c设置了一组api接口函数,我们只需要提供基础的IO驱动程序,由api接口函数调用后,接可以LoRa物理应用层的初始化。
1.板级底层驱动
1.1 添加底层驱动文件
打开我们之前创建的工程,找到radio_board_if.c,可以看到文件有很多warning提示我们创建自己的驱动函数。
1.1.1 创建文件
接下来我们先创建一个board_driver.c和board_driver.h文件,用来存放板子的驱动文件,并将board_driver.c添加到Application/User/SubGHz_Phy/Target目录下。
按照同样的操作创建board_driver.h文件。
1.1.2 将文件添加至工程
由于我们的board_driver.h文件保存SubGHz_Phy/Target目录下,工程生成时已经自动添加了这个文件夹的引用路径。如果你保存在其他路径,则需要手动添加下路径,如下图:
1.2 添加IO驱动程序
1.2.1 宏定义
board_driver.h:
#ifndef _BOARD_DRIVER_H_
#define _BOARD_DRIVER_H_#ifdef __cplusplusextern "C" {#endif#include "stm32wlxx.h"#include "radio_board_if.h"//在下面添加宏定义,变量声明,函数声明#ifdef __cplusplus
}
#endif#endif /* _BOARD_DRIVER_H_ */
先添加宏定义,防止重复调用报错,同时引用stm32wlxx.h,方便c文件初始化IO。
现在先添加一组错误代码定义,用于驱动初始化返回错误信息。
/* Common Error codes */
#define BSP_ERROR_NONE 0
#define BSP_ERROR_NO_INIT -1
#define BSP_ERROR_WRONG_PARAM -2
#define BSP_ERROR_BUSY -3
#define BSP_ERROR_PERIPH_FAILURE -4
#define BSP_ERROR_COMPONENT_FAILURE -5
#define BSP_ERROR_UNKNOWN_FAILURE -6
#define BSP_ERROR_UNKNOWN_COMPONENT -7
#define BSP_ERROR_BUS_FAILURE -8
#define BSP_ERROR_CLOCK_FAILURE -9
#define BSP_ERROR_MSP_FAILURE -10
#define BSP_ERROR_FEATURE_NOT_SUPPORTED -11
添加射频开关控制引脚宏定义,用于初始化调用。
/** @defgroup STM32WLXX_NUCLEO_RADIO_LOW_LEVEL_RFSWITCH RADIO LOW LEVEL RF SWITCH Constants* @{*/ #define RF_SW_CTRL3_PIN GPIO_PIN_3
#define RF_SW_CTRL3_GPIO_PORT GPIOC
#define RF_SW_CTRL3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define RF_SW_CTRL3_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE()#define RF_SW_CTRL1_PIN GPIO_PIN_4
#define RF_SW_CTRL1_GPIO_PORT GPIOC
#define RF_SW_CTRL1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define RF_SW_RX_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE()#define RF_SW_CTRL2_PIN GPIO_PIN_5
#define RF_SW_CTRL2_GPIO_PORT GPIOC
#define RF_SW_CTRL2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define RF_SW_CTRL2_GPIO_CLK_DISABLE() __HAL_RCC_GPIOC_CLK_DISABLE()#define RF_TCXO_VCC_PIN GPIO_PIN_0
#define RF_TCXO_VCC_GPIO_PORT GPIOB
#define RF_TCXO_VCC_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define RF_TCXO_VCC_CLK_DISABLE() __HAL_RCC_GPIOB_CLK_DISABLE()
1.2.2 射频开关初始化函数
board_driver.c :
#include "board_driver.h"
先引用头文件,接下来添加IO初始化驱动。
首先添加射频开关初始化和复位代码。
/*** @brief Init Radio Switch * @retval BSP status*/
int32_t BSP_RADIO_Init(void)
{GPIO_InitTypeDef gpio_init_structure = {0};/* Enable the Radio Switch Clock */RF_SW_CTRL3_GPIO_CLK_ENABLE();/* Configure the Radio Switch pin */gpio_init_structure.Pin = RF_SW_CTRL1_PIN;gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP;gpio_init_structure.Pull = GPIO_NOPULL;gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;HAL_GPIO_Init(RF_SW_CTRL1_GPIO_PORT, &gpio_init_structure);gpio_init_structure.Pin = RF_SW_CTRL2_PIN;HAL_GPIO_Init(RF_SW_CTRL2_GPIO_PORT, &gpio_init_structure);gpio_init_structure.Pin = RF_SW_CTRL3_PIN;HAL_GPIO_Init(RF_SW_CTRL3_GPIO_PORT, &gpio_init_structure);HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET); return BSP_ERROR_NONE;
}
/*** @brief DeInit Radio Swicth* @retval BSP status*/
int32_t BSP_RADIO_DeInit(void)
{RF_SW_CTRL3_GPIO_CLK_ENABLE();/* Turn off switch */HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET); /* DeInit the Radio Switch pin */HAL_GPIO_DeInit(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN);HAL_GPIO_DeInit(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN);HAL_GPIO_DeInit(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN);return BSP_ERROR_NONE;
}
1.2.3 射频开关控制函数
添加射频开关控制函数,要注意的是,射频控制函数是有输入变量的。
所以射频开关有4种状态,可以用switch()选择语句进行判断。
/*** @brief Configure Radio Switch.* @param Config: Specifies the Radio RF switch path to be set. * This parameter can be one of following parameters:* @arg RBI_SWITCH_OFF* @arg RBI_SWITCH_RX* @arg RBI_SWITCH_RFO_LP* @arg RBI_SWITCH_RFO_HP* @retval BSP status*/
int32_t BSP_RADIO_ConfigRFSwitch(RBI_Switch_TypeDef Config)
{switch (Config){case RBI_SWITCH_OFF:{/* Turn off switch */HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_RESET);HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET);HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET);break; }case RBI_SWITCH_RX:{/*Turns On in Rx Mode the RF Swicth */HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET);HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_RESET); break;}case RBI_SWITCH_RFO_LP:{/*Turns On in Tx Low Power the RF Swicth */HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET);HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET); break;}case RBI_SWITCH_RFO_HP:{/*Turns On in Tx High Power the RF Swicth */HAL_GPIO_WritePin(RF_SW_CTRL3_GPIO_PORT, RF_SW_CTRL3_PIN, GPIO_PIN_SET);HAL_GPIO_WritePin(RF_SW_CTRL1_GPIO_PORT, RF_SW_CTRL1_PIN, GPIO_PIN_RESET); HAL_GPIO_WritePin(RF_SW_CTRL2_GPIO_PORT, RF_SW_CTRL2_PIN, GPIO_PIN_SET); break;}default:break; } return BSP_ERROR_NONE;
}
因为TSC_WL_EVK开发板没有关于发送电路,TXCO、DCDC的硬件切换电路,所以保持默认即可。
1.2.4 驱动函数声明
完成函数后,需要在board_driver.h 添加函数声明。
board_driver.h :
/*** @brief Init Radio Switch * @retval BSP status*/
int32_t BSP_RADIO_Init(void);/*** @brief DeInit Radio Swicth* @retval BSP status*/
int32_t BSP_RADIO_DeInit(void);/*** @brief Configure Radio Switch.* @param Config: Specifies the Radio RF switch path to be set. * This parameter can be one of following parameters:* @arg RBI_SWITCH_OFF* @arg RBI_SWITCH_RX* @arg RBI_SWITCH_RFO_LP* @arg RBI_SWITCH_RFO_HP* @retval BSP status*/
int32_t BSP_RADIO_ConfigRFSwitch(RBI_Switch_TypeDef Config);
1.3 api接口函数调用底层驱动
1.3.1 引入底层驱动头文件
radio_board_if.c :
#include "board_driver.h"
在radio_boarad_if.c中添加我们刚才写好的驱动程序头文件。
注意:在STM32CubeMX生成的C文件中,我们添加代码要放在_/XXX XXX BEGIN XXX/_和
_/XXX XXX END XXX/ _中间,不然下次我们打开STM32CubeMX修改配置文件,再重新生成代码的时候,这注释外的代码会被删除掉。
1.3.2 修改api接口函数
先删除radio_board_if.c和radio_board_if.h所有的warning警告:
#warning user to provide its board code or to call his board driver functions
#warning user to provide its board definitions pins
radio_board_if.c :
1.修改RBI_Init()射频初始化函数
int32_t RBI_Init(void)
{/* USER CODE BEGIN RBI_Init_1 *//* USER CODE END RBI_Init_1 */
#if defined(USE_BSP_DRIVER)/* Important note: BSP code is board dependent* STM32WL_Nucleo code can be found* either in STM32CubeWL package under Drivers/BSP/STM32WLxx_Nucleo/* or at https://github.com/STMicroelectronics/STM32CubeWL/tree/main/Drivers/BSP/STM32WLxx_Nucleo/* 1/ For User boards, the BSP/STM32WLxx_Nucleo/ directory can be copied and replaced in the project. The copy must then be updated depending:* on board RF switch configuration (pin control, number of port etc)* on TCXO configuration* on DC/DC configuration* on maximum output power that the board can deliver*/return BSP_RADIO_Init();
#else/* 2/ Or implement RBI_Init here */int32_t retcode = 0;/* USER CODE BEGIN RBI_Init_2 */retcode = BSP_RADIO_Init();/* USER CODE END RBI_Init_2 */return retcode;
#endif /* USE_BSP_DRIVER */
}
其实修改的地方只有1处,#warning 替换成我们写的驱动函数。其他函数修改的地方类似
2.修改 RBI_DeInit()复位函数
int32_t RBI_DeInit(void)
{/* USER CODE BEGIN RBI_DeInit_1 *//* USER CODE END RBI_DeInit_1 */
#if defined(USE_BSP_DRIVER)/* Important note: BSP code is board dependent* STM32WL_Nucleo code can be found* either in STM32CubeWL package under Drivers/BSP/STM32WLxx_Nucleo/* or at https://github.com/STMicroelectronics/STM32CubeWL/tree/main/Drivers/BSP/STM32WLxx_Nucleo/* 1/ For User boards, the BSP/STM32WLxx_Nucleo/ directory can be copied and replaced in the project. The copy must then be updated depending:* on board RF switch configuration (pin control, number of port etc)* on TCXO configuration* on DC/DC configuration* on maximum output power that the board can deliver*/return BSP_RADIO_DeInit();
#else/* 2/ Or implement RBI_DeInit here */int32_t retcode = 0;/* USER CODE BEGIN RBI_DeInit_2 */retcode = BSP_RADIO_DeInit();/* USER CODE END RBI_DeInit_2 */return retcode;
#endif /* USE_BSP_DRIVER */
}
3.修改RBI_ConfigRFSwitch()开关控制函数
int32_t RBI_ConfigRFSwitch(RBI_Switch_TypeDef Config)
{/* USER CODE BEGIN RBI_ConfigRFSwitch_1 *//* USER CODE END RBI_ConfigRFSwitch_1 */
#if defined(USE_BSP_DRIVER)/* Important note: BSP code is board dependent* STM32WL_Nucleo code can be found* either in STM32CubeWL package under Drivers/BSP/STM32WLxx_Nucleo/* or at https://github.com/STMicroelectronics/STM32CubeWL/tree/main/Drivers/BSP/STM32WLxx_Nucleo/* 1/ For User boards, the BSP/STM32WLxx_Nucleo/ directory can be copied and replaced in the project. The copy must then be updated depending:* on board RF switch configuration (pin control, number of port etc)* on TCXO configuration* on DC/DC configuration* on maximum output power that the board can deliver*/return BSP_RADIO_ConfigRFSwitch((BSP_RADIO_Switch_TypeDef) Config);
#else/* 2/ Or implement RBI_ConfigRFSwitch here */int32_t retcode = 0;/* USER CODE BEGIN RBI_ConfigRFSwitch_2 */retcode = BSP_RADIO_ConfigRFSwitch((RBI_Switch_TypeDef) Config);/* USER CODE END RBI_ConfigRFSwitch_2 */return retcode;
#endif /* USE_BSP_DRIVER */
}
剩下的RBI_GetTxConfig()、RBI_IsTCXO()、RBI_IsDCDC()、RBI_GetRFOMaxPowerConfig(),因为没有硬件切换电路,所以只需要删除#warning警告语句即可。
2.应用层函数
应用层函数主要修改subghz_phy_app.c函数,修改时要注意代码应设置在 /XXX XXX BEGIN XXX/ 和 /XXX XXX END XXX/ 中间。
2.1 参数预定义
2.1.1 私有类型定义
subghz_phy_app.c:
/* USER CODE BEGIN PTD */
typedef enum
{RX,RX_TIMEOUT,RX_ERROR,TX,TX_TIMEOUT,
} States_t;
/* USER CODE END PTD */
定义一组枚举型变量,用于区分射频发送的不同状态。
2.1.2 宏定义
subghz_phy_app.c:
/* USER CODE BEGIN PD *//*Timeout*/
#define RX_TIMEOUT_VALUE 3000
#define TX_TIMEOUT_VALUE 3000/* PING string*/
#define PING "PING"
/* PONG string*/
#define PONG "PONG"/*Size of the payload to be sent*/
/* Size must be greater of equal the PING and PONG*/
#define MAX_APP_BUFFER_SIZE 255#if (PAYLOAD_LEN > MAX_APP_BUFFER_SIZE)
#error PAYLOAD_LEN must be less or equal than MAX_APP_BUFFER_SIZE
#endif /* (PAYLOAD_LEN > MAX_APP_BUFFER_SIZE) *//* wait for remote to be in Rx, before sending a Tx frame*/
#define RX_TIME_MARGIN 1000/* USER CODE END PD */
宏定义里定义了发送和接收的帧超时时间,接收间隔时间。
MAX_APP_BUFFER_SIZE 为正常负载长度(payload len)下,LoRa所能接收的最大数据内容长度。
2.1.3 私有变量
subghz_phy_app.c:
/* USER CODE BEGIN PV *//*Ping Pong FSM states */
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;/* device state. Master: true, Slave: false*/
bool isMaster = true;/* random delay to make sure 2 devices will sync*/
/* the closest the random delays are, the longer it willtake for the devices to sync when started simultaneously*/
static int32_t random_delay;/* USER CODE END PV */
State : 射频状态机标志位;
BufferRx /BufferTx : 接收和发送数据,用于存储接收和发送数据;
RxBufferSize : 记录最后一个接收包的数据大小;
RssiValue : 记录最有一个接收包的RSSI值;
SnrValue : 记录最有一个接收包的SNR值;
isMaster : 设备标志位,用于区分主从机;
random_delay : 随机生成的延迟数,用于主从机设备同步。
2.1.4 私有函数声明
subghz_phy_app.c:
/* USER CODE BEGIN PFP *//*** @brief PingPong state machine implementation*/
static void PingPong_Process(void);/* USER CODE END PFP */
PingPong主程序,RTC与SEQ任务管理器结合,实现不同状态机的调度。
2.2 CallBack函数
STM32CubeMX生成的代码中,预留了不同状态的空白回叫函数,我们按照自己的需要,填充不同的代码。
2.2.1 初始化
subghz_phy_app.c:
void SubghzApp_Init(void)
{/* USER CODE BEGIN SubghzApp_Init_1 */APP_LOG(TS_OFF, VLEVEL_M, "\n\rPING PONG Test\n\r");/* USER CODE END SubghzApp_Init_1 *//* Radio initialization */RadioEvents.TxDone = OnTxDone;RadioEvents.RxDone = OnRxDone;RadioEvents.TxTimeout = OnTxTimeout;RadioEvents.RxTimeout = OnRxTimeout;RadioEvents.RxError = OnRxError;Radio.Init(&RadioEvents);/* USER CODE BEGIN SubghzApp_Init_2 *//*calculate random delay for synchronization*/random_delay = (Radio.Random()) >> 22; /*10bits random e.g. from 0 to 1023 ms*//* Radio Set frequency */Radio.SetChannel(RF_FREQUENCY);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);/*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.Rx(RX_TIMEOUT_VALUE + random_delay);/*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, PingPong_Process);/* USER CODE END SubghzApp_Init_2 */
}
2.2.2 发送完成
subghz_phy_app.c:
static void OnTxDone(void)
{/* USER CODE BEGIN OnTxDone */APP_LOG(TS_ON, VLEVEL_L, "OnTxDone\n\r");/* Update the State of the FSM*/State = TX;/* Run PingPong process in background*/UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0);/* USER CODE END OnTxDone */
}
2.2.3 接收完成
subghz_phy_app.c:
static void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t LoraSnr_FskCfo)
{/* USER CODE BEGIN OnRxDone */APP_LOG(TS_ON, VLEVEL_L, "OnRxDone\n\r");APP_LOG(TS_ON, VLEVEL_L, "RssiValue= %d dBm, SnrValue= %ddB\n\r", rssi, LoraSnr_FskCfo);/* Record payload Signal to noise ratio in Lora*/SnrValue = LoraSnr_FskCfo;/* Update the State of the FSM*/State = RX;/* Clear BufferRx*/memset(BufferRx, 0, MAX_APP_BUFFER_SIZE);/* Record payload size*/RxBufferSize = size;if (RxBufferSize <= MAX_APP_BUFFER_SIZE){memcpy(BufferRx, payload, RxBufferSize);}/* Record Received Signal Strength*/RssiValue = rssi;/* Record payload content*/APP_LOG(TS_ON, VLEVEL_H, "payload. size=%d \n\r", size);for (int i = 0; i < PAYLOAD_LEN; i++){APP_LOG(TS_OFF, VLEVEL_H, "%02X", BufferRx[i]);if (i % 16 == 15){APP_LOG(TS_OFF, VLEVEL_H, "\n\r");}}APP_LOG(TS_OFF, VLEVEL_H, "\n\r");/* Run PingPong process in background*/UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0);/* USER CODE END OnRxDone */
}
2.2.4 发送超时
subghz_phy_app.c:
static void OnTxTimeout(void)
{/* USER CODE BEGIN OnTxTimeout */APP_LOG(TS_ON, VLEVEL_L, "OnTxTimeout\n\r");/* Update the State of the FSM*/State = TX_TIMEOUT;/* Run PingPong process in background*/UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0);/* USER CODE END OnTxTimeout */
}
2.2.5 接收超时
subghz_phy_app.c:
static void OnRxTimeout(void)
{/* USER CODE BEGIN OnRxTimeout */APP_LOG(TS_ON, VLEVEL_L, "OnRxTimeout\n\r");/* Update the State of the FSM*/State = RX_TIMEOUT;/* Run PingPong process in background*/UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0);/* USER CODE END OnRxTimeout */
}
2.2.6 接收错误
subghz_phy_app.c:
static void OnRxError(void)
{/* USER CODE BEGIN OnRxError */APP_LOG(TS_ON, VLEVEL_L, "OnRxError\n\r");/* Update the State of the FSM*/State = RX_ERROR;/* Run PingPong process in background*/UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0);/* USER CODE END OnRxError */
}
2.3 PingPong 程序实现
2.3.1 程序逻辑图
2.3.2 程序内容
/* USER CODE BEGIN PrFD */
static void PingPong_Process(void)
{Radio.Sleep();switch (State){case RX:if (isMaster == true){if (RxBufferSize > 0){if (strncmp((const char *)BufferRx, PONG, sizeof(PONG) - 1) == 0){/* Add delay between RX and TX */HAL_Delay(Radio.GetWakeupTime() + RX_TIME_MARGIN);/* master sends PING*/APP_LOG(TS_ON, VLEVEL_L, "...""PING""\n\r");APP_LOG(TS_ON, VLEVEL_L, "Master Tx start\n\r");memcpy(BufferTx, PING, sizeof(PING) - 1);Radio.Send(BufferTx, PAYLOAD_LEN);}else if (strncmp((const char *)BufferRx, PING, sizeof(PING) - 1) == 0){/* A master already exists then become a slave */isMaster = false;APP_LOG(TS_ON, VLEVEL_L, "Slave Rx start\n\r");Radio.Rx(RX_TIMEOUT_VALUE);}else /* valid reception but neither a PING or a PONG message */{/* Set device as master and start again */isMaster = true;APP_LOG(TS_ON, VLEVEL_L, "Master Rx start\n\r");Radio.Rx(RX_TIMEOUT_VALUE);}}}else{if (RxBufferSize > 0){if (strncmp((const char *)BufferRx, PING, sizeof(PING) - 1) == 0){/* Add delay between RX and TX */HAL_Delay(Radio.GetWakeupTime() + RX_TIME_MARGIN);/*slave sends PONG*/APP_LOG(TS_ON, VLEVEL_L, "...""PONG""\n\r");APP_LOG(TS_ON, VLEVEL_L, "Slave Tx start\n\r");memcpy(BufferTx, PONG, sizeof(PONG) - 1);Radio.Send(BufferTx, PAYLOAD_LEN);}else /* valid reception but not a PING as expected */{/* Set device as master and start again */isMaster = true;APP_LOG(TS_ON, VLEVEL_L, "Master Rx start\n\r");Radio.Rx(RX_TIMEOUT_VALUE);}}}break;case TX:APP_LOG(TS_ON, VLEVEL_L, "Rx start\n\r");Radio.Rx(RX_TIMEOUT_VALUE);break;case RX_TIMEOUT:case RX_ERROR:if (isMaster == true){/* Send the next PING frame *//* Add delay between RX and TX*//* add random_delay to force sync between boards after some trials*/HAL_Delay(Radio.GetWakeupTime() + RX_TIME_MARGIN + random_delay);APP_LOG(TS_ON, VLEVEL_L, "Master Tx start\n\r");/* master sends PING*/memcpy(BufferTx, PING, sizeof(PING) - 1);Radio.Send(BufferTx, PAYLOAD_LEN);}else{APP_LOG(TS_ON, VLEVEL_L, "Slave Rx start\n\r");Radio.Rx(RX_TIMEOUT_VALUE);}break;case TX_TIMEOUT:APP_LOG(TS_ON, VLEVEL_L, "Slave Rx start\n\r");Radio.Rx(RX_TIMEOUT_VALUE);break;default:break;}}
3.烧录与调试
3.1编译
程序编写完成后,点击魔术棒按钮,打开选项设置。
如果不勾选MicroLIB缺省C库,串口会出现无法打印的情况。
点击全部编译按钮。
3.2 烧录
在烧录前,需要连接好ST-Link,和MicroUSB数据线。
我使用的是SWD调试,所以只需要接4根线就行。
等待编译完成后,点击魔术棒按钮,打开烧录器设置界面,根据你使用的烧录器进行设置,我这里使用的是ST-LINK。
选择好ST-Link后,进入烧录器设置界面。
勾选共享设置,方便其他软件也能识别到ST-Link。
勾选下载后复位选项,这样程序下载完成后,板子就会自动复位,并开始执行程序。
设置完成后记得点确定按钮保存。
点击下载按钮,便可以完成程序烧录。
两块板子烧录的是同一个程序,板子会根据接收先后时间自动区分主从机。
烧录器常见问题:
1.识别不到ST-LINK,需要查看驱动,供电是否正常;
2.识别不到SWDIO,检查接线是否正确,或是检查板子正在运行的程序有没有打开SWD调试引脚,如果没有,需要按住复位键松手后,立即烧录程序。
3.3 结果与调试
程序烧录后,可以通过查看串口打印信息,查看两块板子是否正常通讯。
选择COM口和合适的波特率后,需要点击打开串口按钮,才能看到串口打印信息。
如果出现错误或超时信息,可以使用DEBUG按钮,对板子进行调试。
Ping Pong 程序常见错误归纳:
1.random_delay随机延时为0:晶振不起振,或者晶振参数设置有问题;
2.打印信息顺序错乱: 1):可能存在中断冲突,不同程序出现了抢占,此点最容易忽略的是延时程序; 2):printf和APP_LOG 打印混用。
3.发送或接送超时 : 1):两块板子的发送频率,扩频因子等基础参数不同,一般原因是使用了不同的程序; 2):天线没接好,不匹配,只有一块板子,或者周围有较强电磁辐射干扰源; 3):板子硬件的射频匹配电路与射频程序不一致,例如,硬件用的是Low
Power的巴伦匹配电路,跑的程序却是High Power的程序。
LoRa入门(2)—— PingPong测试(软件篇)相关推荐
- android bsp入门到精通,网管教程:从入门到精通(软件篇).pdf
网管教程:从入门到精通(软件篇) Ver 1.1 Feihon (609095519 )整理 2010-4-26 (资料来自 IT 网绚技术赸级群癿分享,本人仅提供整理,原作者未知 ) 网管教程:从入 ...
- 英语中学生测试软件,中考英语口语测试软件.doc
中考英语口语测试软件 中考英语口语测试软件 篇一:2014中考英语口语考试模拟试题 2016年全国中考英语口语考试要求大纲 一.朗读(5分) 给你50秒钟时间准备朗读.当听到"开始录音&qu ...
- 电路原理仿真测试软件,测试工具篇-运放测试电路仿真《TINA-TI》
©版权声明:本文为[看我哒 www.kanwoda.com]原创文章,转载时请注明出处! 出门向左,感谢Jerry Gao的分享<测试基础篇-电容与电感对电流测试的影响>,文中介绍了用LT ...
- 无风扇cpu测试软件,5分钟就蓝屏!无风扇散热评测之办公稳定性篇
特别声明:本文是针对CPU进行非常规性的被动散热评测,检验了目前入门级双核CPU在严重恶劣的散热条件下的实际表现情况.由于不同的处理器厂商在特殊的非常规环境下采用的过热保护机制并不相同,所以本文只是提 ...
- dt测试软件的学习心得,无线网络优化dt测试心得_适合新手入门,高手进阶_5年项目经验实战经验.docx...
无线网络优化DT测试心得_适合新手入门,高手进阶_5年项目经验实战经验 无线网络优化DT测试心得_适合新手入门,高手进阶_5年项目经验实战经验 路测中不常见的问题和个人心得 1.深井子镇投诉测试报告 ...
- 零基础入门Jetson Nano——软件篇
Jetson Nano学习--软件篇 前言 一.General Commands-解析 二.File & Directory Commands-解析 1.绝对路径和相对路径 2.根目录 3.文 ...
- ART-Pi入门篇——(一)软件篇
ART-Pi入门篇--(一)软件篇 目录 ART-Pi入门篇--(一)软件篇 RTT Studio开发 新建工程以及下载程序 固件下载 问题 MDK开发 1.库的链接 2.添加下载算法 ART-Pi ...
- 导航信号测试用什么软件,专业GPS测试软件 VisualGPSXP入门
专业测试软件VisualGPSXP简介 通常我们购买蓝牙GPS后最头疼的问题就是这款产品是否真的如厂家宣传的那么好,尤其是接收效果.以往我们会使用GPS Viewer等查看星图的软件进行GPS的信号测 ...
- 32位mysql安装包_《MySQL 入门教程》第 02 篇 MySQL 安装
文章来源:<MySQL 入门教程>第 02 篇 MySQL 安装 原文作者:不剪发的Tony老师 来源平台:CSDN 上一篇我们了解了什么是MySQL数据库. 本文介绍如何在 Window ...
最新文章
- Linux中/etc/resolv.conf文件简析
- AAAI 2021线下论文预讲会讲者征集
- 2021夏季每日一题 【week1 未完结】
- 神策数据产品演进及行业延展实践
- 最近学习linux-c的编程
- sqlite3x library
- javascript与xml实例应用
- 强连通分量:洛谷P3387 模板:缩点
- python发短信脚本_python脚本发送短信
- syslog-ng按源ip保存记录
- mysql 安装目录配置_linux 下mysql安装,目录配置
- 遥感原理与应用总结——第一章:遥感原理的基本概念
- 5G时代将给智能营销笔记本带来什么样的改变
- 星露谷物语 android 中文版,星露谷物语手机版
- Go Web快速开发框架 Fiber
- yarn部署及Linux的OOM、clean机制
- 双系统重装Ubuntu
- java赫夫曼编码(含赫夫曼编码代码)
- 移动互联网时代的大数据挖掘和精准营销
- centos7 数据库 Greenplum 的单机安装及异常解决方案
热门文章
- 解决CF黑边和无法全屏
- 第04课:生活中的单例模式——你是我生命的唯一
- 炮炮兵可爱桌面壁纸 高清
- PHP实现好友生日邮件提醒
- PHP目录函数 readdir (从目录句柄中读取条目)
- python重量转换_Python基础之注释,算数运算符,变量,输入和格式化输出
- 背景图延迟加载(lazyload)技术
- 借“窄喉”之力,边缘计算期待“井喷”
- 如何处理代码中的魔术数字(Magic Number)
- linux外汇行情软件,Dukascopy外汇官方高质量Tick数据下载python源码