前言
本节我们在PingPong例程的基础上完成STM32WLE5内部的温度,VBAT电压及外部ADC的定时采集上报。介绍了STM32WlE5的ADC采样、软件定时和Lora私有协议发送和接收的方法,便于使用STM32WLE5构建与传统的串口Lora模块类似的点对点,点对多点的采集上报应用。
由于STM32WLE5集成的是Semtech的SX1262内核,因此在频点一致、Lora参数一致的情况下STM32WLE5能够与SX1268,SX1262实现互联互通。
一、数据上报格式
作为私有协议的Lora数据传输,为了方便管理,我们需要定义一个简单数据包格式。数据格式采用常规的TLV格式,定义如下:
数据由帧头、数据包长度、目标ID、本机ID、数据类型、具体数据和校验和组成。
帧头使用0xA5、0x7E;
数据包长度为整个数据包的长度,包括帧头和检验和;
远端ID为目标ID,设定0xFFFF为广播地址,集中器都可以解析;
本端ID为本机ID,标识不同的传感器;
类型部分可以定义不同的传感器或者不同数据格式,集中器根据数据类型进行相应格式的数据解析;
MCU温度为STM32WLE5内部的温度采集,数据格式为16bit q7.8;
BAT电压为LM401的输入电压,单位mV;
ADC电压为PA11引脚输入的电压,单位mV;
校验和为累加和。
发送端发送时需要指明远端ID,也可用0xFFFF作广播。
接收端匹配ID,是发给自己的或者广播地址的解析并输出,不是自己的则丢弃。
完成该例子需要两块LM401评估板。
二、 ADC采集
ADC采集涉及adc.c和adc_if.c两个文件。
1、ADC的初始化
adc.c为CubeMx生产的初始化文件。为了提高采集精度,我们使用过采样处理。主要修改MX_ADC_Init(),修改后的adc.c如下:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "adc.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
ADC_HandleTypeDef hadc;
/* ADC init function */
void MX_ADC_Init(void)
{
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.NbrOfConversion = 1;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_160CYCLES_5;
hadc.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_160CYCLES_5;
// hadc.Init.OversamplingMode = DISABLE;
//使能过采样,16倍过采样
hadc.Init.OversamplingMode = ENABLE;
hadc.Init.Oversampling.Ratio = ADC_OVERSAMPLING_RATIO_16;
hadc.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_4;
hadc.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_SINGLE_TRIGGER;
hadc.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC_Init 2 */
/* USER CODE END ADC_Init 2 */
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(adcHandle->Instance==ADC)
{
/* USER CODE BEGIN ADC_MspInit 0 */
/* USER CODE END ADC_MspInit 0 */
/* ADC clock enable */
__HAL_RCC_ADC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**ADC GPIO Configuration
PA11 ------> ADC_IN7
*/
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN ADC_MspInit 1 */
/* USER CODE END ADC_MspInit 1 */
}
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{
if(adcHandle->Instance==ADC)
{
/* USER CODE BEGIN ADC_MspDeInit 0 */
/* USER CODE END ADC_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ADC_CLK_DISABLE();
/**ADC GPIO Configuration
PA11 ------> ADC_IN7
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11);
/* USER CODE BEGIN ADC_MspDeInit 1 */
/* USER CODE END ADC_MspDeInit 1 */
}
}
2、ADC采集
参照ST的例程为了低功耗处理构建ADC_ReadChannels(uint32_t channel)函数,每次获取adc数据是先进行adc的初始化,再进行数据采集,最后释放adc资源。adc_if.c内容如下:
/* External variables ---------------------------------------------------------*/
/**
* @brief ADC handle
*/
extern ADC_HandleTypeDef hadc;
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
#define TEMPSENSOR_TYP_CAL1_V (( int32_t) 760) /*!< Internal temperature sensor, parameter V30 (unit: mV). Refer to device datasheet for min/typ/max values. */
#define TEMPSENSOR_TYP_AVGSLOPE (( int32_t) 2500) /*!< Internal temperature sensor, parameter Avg_Slope (unit: uV/DegCelsius). Refer to device datasheet for min/typ/max values. */
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/**
* @brief This function reads the ADC channel
* @param channel channel number to read
* @return adc measured level value
*/
static uint32_t ADC_ReadChannels(uint32_t channel);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Exported functions --------------------------------------------------------*/
/* USER CODE BEGIN EF */
/* USER CODE END EF */
void SYS_InitMeasurement(void)
{
/* USER CODE BEGIN SYS_InitMeasurement_1 */
/* USER CODE END SYS_InitMeasurement_1 */
hadc.Instance = ADC;
/* USER CODE BEGIN SYS_InitMeasurement_2 */
/* USER CODE END SYS_InitMeasurement_2 */
}
void SYS_DeInitMeasurement(void)
{
/* USER CODE BEGIN SYS_DeInitMeasurement_1 */
/* USER CODE END SYS_DeInitMeasurement_1 */
}
int16_t SYS_GetTemperatureLevel(void)
{
/* USER CODE BEGIN SYS_GetTemperatureLevel_1 */
/* USER CODE END SYS_GetTemperatureLevel_1 */
int16_t temperatureDegreeC = 0;
uint32_t measuredLevel = 0;
uint16_t batteryLevelmV = SYS_GetBatteryLevel();
measuredLevel = ADC_ReadChannels(ADC_CHANNEL_TEMPSENSOR);
/* convert ADC level to temperature */
/* check whether device has temperature sensor calibrated in production */
if (((int32_t)*TEMPSENSOR_CAL2_ADDR - (int32_t)*TEMPSENSOR_CAL1_ADDR) != 0)
{
/* Device with temperature sensor calibrated in production:
use device optimized parameters */
temperatureDegreeC = __LL_ADC_CALC_TEMPERATURE(batteryLevelmV,
measuredLevel,
LL_ADC_RESOLUTION_12B);
}
else
{
/* Device with temperature sensor not calibrated in production:
use generic parameters */
temperatureDegreeC = __LL_ADC_CALC_TEMPERATURE_TYP_PARAMS(TEMPSENSOR_TYP_AVGSLOPE,
TEMPSENSOR_TYP_CAL1_V,
TEMPSENSOR_CAL1_TEMP,
batteryLevelmV,
measuredLevel,
LL_ADC_RESOLUTION_12B);
}
APP_LOG(TS_ON, VLEVEL_L, "temp= %d\n\r", temperatureDegreeC);
/* from int16 to q8.7*/
temperatureDegreeC <<= 8;
return (int16_t) temperatureDegreeC;
/* USER CODE BEGIN SYS_GetTemperatureLevel_2 */
/* USER CODE END SYS_GetTemperatureLevel_2 */
}
uint16_t SYS_GetBatteryLevel(void)
{
/* USER CODE BEGIN SYS_GetBatteryLevel_1 */
/* USER CODE END SYS_GetBatteryLevel_1 */
uint16_t batteryLevelmV = 0;
uint32_t measuredLevel = 0;
measuredLevel = ADC_ReadChannels(ADC_CHANNEL_VREFINT);
if (measuredLevel == 0)
{
batteryLevelmV = 0;
}
else
{
if ((uint32_t)*VREFINT_CAL_ADDR != (uint32_t)0xFFFFU)
{
/* Device with Reference voltage calibrated in production:
use device optimized parameters */
batteryLevelmV = __LL_ADC_CALC_VREFANALOG_VOLTAGE(measuredLevel,
ADC_RESOLUTION_12B);
}
else
{
/* Device with Reference voltage not calibrated in production:
use generic parameters */
batteryLevelmV = (VREFINT_CAL_VREF * 1510) / measuredLevel;
}
}
return batteryLevelmV;
/* USER CODE BEGIN SYS_GetBatteryLevel_2 */
/* USER CODE END SYS_GetBatteryLevel_2 */
}
/* Private Functions Definition -----------------------------------------------*/
/* USER CODE BEGIN PrFD */
uint16_t GetADC_PA11(void)
{
return ADC_ReadChannels(ADC_CHANNEL_7);
}
/* USER CODE END PrFD */
static uint32_t ADC_ReadChannels(uint32_t channel)
{
/* USER CODE BEGIN ADC_ReadChannels_1 */
/* USER CODE END ADC_ReadChannels_1 */
uint32_t ADCxConvertedValues = 0;
ADC_ChannelConfTypeDef sConfig = {0};
MX_ADC_Init();
/* Start Calibration */
if (HAL_ADCEx_Calibration_Start(&hadc) != HAL_OK)
{
Error_Handler();
}
/* Configure Regular Channel */
sConfig.Channel = channel;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_ADC_Start(&hadc) != HAL_OK)
{
/* Start Error */
Error_Handler();
}
/** Wait for end of conversion */
HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY);
/** Wait for end of conversion */
HAL_ADC_Stop(&hadc) ; /* it calls also ADC_Disable() */
ADCxConvertedValues = HAL_ADC_GetValue(&hadc);
HAL_ADC_DeInit(&hadc);
return ADCxConvertedValues;
/* USER CODE BEGIN ADC_ReadChannels_2 */
/* USER CODE END ADC_ReadChannels_2 */
}
对应的adc_if.h如下:
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __ADC_IF_H__
#define __ADC_IF_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "adc.h"
#include "platform.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/**
* @brief Battery level in mV
*/
#define BAT_CR2032 ((uint32_t) 3000)
/**
* @brief Maximum battery level in mV
*/
#define VDD_BAT BAT_CR2032
/**
* @brief Minimum battery level in mV
*/
#define VDD_MIN 1800
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* External variables --------------------------------------------------------*/
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
/**
* @brief Initializes the ADC input
*/
void SYS_InitMeasurement(void);
/**
* @brief DeInitializes the ADC
*/
void SYS_DeInitMeasurement(void);
/**
* @brief Get the current temperature
* @return value temperature in degree Celsius( q7.8 )
*/
int16_t SYS_GetTemperatureLevel(void);
/**
* @brief Get the current battery level
* @return value battery level in linear scale
*/
uint16_t SYS_GetBatteryLevel(void);
uint16_t GetADC_PA11(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
#ifdef __cplusplus
}
#endif
#endif /* __ADC_IF_H__ */
三、 采集端上报处理流程
初始化射频之后,启动上报定时器,定时周期为SEND_PERIOD_MS。在定时器回调中设置为S_SEND_DATA状态,并设置任务调用
State = S_SEND_DATA;
UTIL_SEQ_SetTask((1 <<CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0);
在任务处理的SendData_Process()中进行数据采集,并计算PA11的ADC对应电压。之后进行组包发送。修改后的subghz_phy_app.c如下:
/* Includes ------------------------------------------------------------------*/
#include "platform.h"
#include "sys_app.h"
#include "subghz_phy_app.h"
#include "radio.h"
#include "app_version.h"
/* USER CODE BEGIN Includes */
#include "stm32_timer.h"
#include "stm32_seq.h"
#include <stdint.h>
#include "utilities_def.h"
#include "adc_if.h"
/* USER CODE END Includes */
/* External variables ---------------------------------------------------------*/
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef enum
{
S_IDLE,
S_RX,
S_RX_TIMEOUT,
S_RX_ERROR,
S_TX,
S_TXing,
S_TX_TIMEOUT,
S_SEND_DATA,
} States_t;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* Configurations */
/*Timeout*/
#define RX_TIMEOUT_VALUE 5000
#define TX_TIMEOUT_VALUE 3000
//上报周期
#define SEND_PERIOD_MS 10000
//本端ID
#define LOCAL_ID 0x01
//远端ID
#define REMOTE_ID 0x10
//数据类型
#define DATA_TYPE 0x01
/*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) */
static RadioEvents_t RadioEvents;
/* USER CODE BEGIN PV */
/*Ping Pong FSM states */
static States_t State = S_IDLE;
/* 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;
/* Led Timers objects*/
//上报定时器
static UTIL_TIMER_Object_t timerSendData;
/* random delay to make sure 2 devices will sync*/
/* the closest the random delays are, the longer it will
take for the devices to sync when started simultaneously*/
static int32_t random_delay;
//采集数据
static int16_t temp_v=0;
static uint16_t bat_v=0;
static uint16_t pa11_v=0;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/*!
* @brief Function to be executed on Radio Tx Done event
*/
static void OnTxDone(void);
/**
* @brief Function to be executed on Radio Rx Done event
* @param payload ptr of buffer received
* @param size buffer size
* @param rssi
* @param LoraSnr_FskCfo
*/
static void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t LoraSnr_FskCfo);
/**
* @brief Function executed on Radio Tx Timeout event
*/
static void OnTxTimeout(void);
/**
* @brief Function executed on Radio Rx Timeout event
*/
static void OnRxTimeout(void);
/**
* @brief Function executed on Radio Rx Error event
*/
static void OnRxError(void);
/* USER CODE BEGIN PFP */
/**
* @brief Function executed on when led timer elapses
* @param context ptr of LED context
*/
static void OnSendDataEvent(void *context);
/**
* @brief PingPong state machine implementation
*/
static void SendData_Process(void);
static uint16_t PackData(uint16_t r_id);
static uint8_t SumCheck(uint8_t *buf,uint8_t len);
/* USER CODE END PFP */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch (GPIO_Pin)
{
case BUTTON_SW1_PIN:
BSP_LED_Toggle(LED_BLUE) ;
HAL_Delay(20);
BSP_LED_Toggle(LED_BLUE) ;
APP_PRINTF("BUTTON SW1\r\n");
break;
case BUTTON_SW2_PIN:
BSP_LED_Toggle(LED_GREEN) ;
HAL_Delay(20);
BSP_LED_Toggle(LED_GREEN) ;
APP_PRINTF("BUTTON SW2\r\n");
break;
case BUTTON_SW3_PIN:
BSP_LED_Toggle(LED_RED) ;
HAL_Delay(20);
BSP_LED_Toggle(LED_RED) ;
APP_PRINTF("BUTTON SW3\r\n");
break;
default:
APP_PRINTF("Unkonw Button\r\n");
break;
}
}
/* Exported functions ---------------------------------------------------------*/
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));
/*创建定时器对象*/
UTIL_TIMER_Create(&timerSendData, 0xFFFFFFFFU, UTIL_TIMER_ONESHOT, OnSendDataEvent, NULL);
/*设置定时周期*/
UTIL_TIMER_SetPeriod(&timerSendData, SEND_PERIOD_MS);
/*启动定时*/
UTIL_TIMER_Start(&timerSendData);
/* 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);
/*设置射频频率*/
Radio.SetChannel(RF_FREQUENCY);
/* Radio configuration */
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);
/* LED initialization*/
BSP_LED_Init(LED_GREEN);
BSP_LED_Init(LED_RED);
BSP_LED_Init(LED_BLUE);
/* 按键 initialization*/
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.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, SendData_Process);
/* USER CODE END SubghzApp_Init_2 */
}
/* USER CODE BEGIN EF */
/* USER CODE END EF */
/* Private functions ---------------------------------------------------------*/
/* 发送完成中断*/
static void OnTxDone(void)
{
/* USER CODE BEGIN OnTxDone */
APP_LOG(TS_ON, VLEVEL_L, "OnTxDone\n\r");
/* Update the State of the FSM*/
State = S_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 */
}
/* 接收完成中断*/
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);
/* Update the State of the FSM*/
State = S_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 */
}
/* 发送超时中断*/
static void OnTxTimeout(void)
{
/* USER CODE BEGIN OnTxTimeout */
APP_LOG(TS_ON, VLEVEL_L, "OnTxTimeout\n\r");
/* Update the State of the FSM*/
State = S_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 */
}
/* 接收超时中断*/
static void OnRxTimeout(void)
{
/* USER CODE BEGIN OnRxTimeout */
APP_LOG(TS_ON, VLEVEL_L, "OnRxTimeout\n\r");
/* Update the State of the FSM*/
State = S_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 */
}
/* 接收错误中断*/
static void OnRxError(void)
{
/* USER CODE BEGIN OnRxError */
APP_LOG(TS_ON, VLEVEL_L, "OnRxError\n\r");
/* Update the State of the FSM*/
State = S_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 */
}
/* USER CODE BEGIN PrFD */
/* 采集发送数据的主函数*/
static void SendData_Process(void)
{
uint16_t temp = 0;
uint16_t sendlen = 0;
//射频休眠
switch (State)
{
case S_IDLE:
case S_TXing:
break;
case S_RX:
Radio.Sleep();
State = S_IDLE;
break;
case S_TX:
BSP_LED_Off(LED_GREEN);
BSP_LED_Off(LED_RED);
Radio.Sleep();
State = S_IDLE;
break;
case S_RX_TIMEOUT:
case S_RX_ERROR:
case S_TX_TIMEOUT:
Radio.Sleep();
State = S_IDLE;
break;
case S_SEND_DATA:
temp_v = SYS_GetTemperatureLevel();
bat_v = SYS_GetBatteryLevel();
temp = GetADC_PA11();
//将PA11的ADC转换成电压,单位mV
pa11_v =__LL_ADC_CALC_DATA_TO_VOLTAGE(bat_v, temp,ADC_RESOLUTION_12B);
APP_LOG(TS_ON, VLEVEL_L, "TemperatureLevel = %d.%d BatteryLevel = %d PA11Level = %d\n\r",temp_v>>8,(uint8_t)temp_v,bat_v,pa11_v);
//组包
sendlen = PackData(REMOTE_ID);
if(sendlen < MAX_APP_BUFFER_SIZE)
Radio.Send(BufferTx,sendlen);
else
APP_PRINTF("The length of the sent data is greater than MAX_APP_BUFFER_SIZE(%d)\n\r",MAX_APP_BUFFER_SIZE);
State = S_TXing;
break;
default:
break;
}
}
static void OnSendDataEvent(void *context)
{
BSP_LED_On(LED_GREEN);
BSP_LED_On(LED_RED);
UTIL_TIMER_Start(&timerSendData);
//空闲状态时进行数据发送
if(State == S_IDLE)
{
State = S_SEND_DATA;
UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_SubGHz_Phy_App_Process), CFG_SEQ_Prio_0);
}
else
{
APP_PRINTF("OnSendDataEvent Error State(%d)\n\r",State);
}
}
static uint16_t PackData(uint16_t r_id)
{
uint8_t len = 0;
uint8_t i = 0;
uint16_t id = LOCAL_ID;
BufferTx[len++]= 0xA5;
BufferTx[len++]= 0x7E;
len++;
memcpy(&BufferTx[len],&r_id,2);
len+=2;
memcpy(&BufferTx[len],&id,2);
len+=2;
BufferTx[len++]= DATA_TYPE;
memcpy(&BufferTx[len],&temp_v,2);
len+=2;
memcpy(&BufferTx[len],&bat_v,2);
len+=2;
memcpy(&BufferTx[len],&pa11_v,2);
len+=2;
//长度
BufferTx[2]= len+1;
BufferTx[len]= SumCheck(BufferTx,len);
len++;
APP_LOG(TS_ON, VLEVEL_L, "%s: payload. size=%d \n\r", __func__,len);
for (int i = 0; i < len; i++)
{
APP_LOG(TS_OFF, VLEVEL_L, "%02X", BufferTx[i]);
if (i % 16 == 15)
{
APP_LOG(TS_OFF, VLEVEL_L, "\n\r");
}
}
APP_LOG(TS_OFF, VLEVEL_L, "\n\r");
return len;
}
static uint8_t SumCheck(uint8_t *buf,uint8_t len)
{
uint8_t i = 0;
uint8_t sum = 0;
for(i = 0;i < len;i++)
{
sum += buf[i];
}
return sum;
}
四、 接收端接收处理流程
接收端主要对接收到的数据进行帧头、长度,ID以及校验和进行比对,都没有问题的情况下进行解析并答应输出。修改后的subghz_phy_app.c如下:
/* Includes ------------------------------------------------------------------*/
#include "platform.h"
#include "sys_app.h"
#include "subghz_phy_app.h"
#include "radio.h"
#include "app_version.h"
/* USER CODE BEGIN Includes */
#include "stm32_timer.h"
#include "stm32_seq.h"
#include "utilities_def.h"
/* USER CODE END Includes */
/* External variables ---------------------------------------------------------*/
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef enum
{
S_IDLE,
S_RX,
S_RX_TIMEOUT,
S_RX_ERROR,
S_TX,
S_TX_TIMEOUT,
} States_t;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* Configurations */
/*Timeout*/
#define RX_TIMEOUT_VALUE 5000
#define TX_TIMEOUT_VALUE 3000
#define SEND_PERIOD_MS 10000
#define LOCAL_ID 0x01
#define Concentrator_ID 0x10
#define DATA_TYPE01 0x01
/*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) */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* Radio events function pointer */
static RadioEvents_t RadioEvents;
/* USER CODE BEGIN PV */
/*Ping Pong FSM states */
static States_t State = S_IDLE;
/* 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 will
take for the devices to sync when started simultaneously*/
static int32_t random_delay;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/*!
* @brief Function to be executed on Radio Tx Done event
*/
static void OnTxDone(void);
/**
* @brief Function to be executed on Radio Rx Done event
* @param payload ptr of buffer received
* @param size buffer size
* @param rssi
* @param LoraSnr_FskCfo
*/
static void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t LoraSnr_FskCfo);
/**
* @brief Function executed on Radio Tx Timeout event
*/
static void OnTxTimeout(void);
/**
* @brief Function executed on Radio Rx Timeout event
*/
static void OnRxTimeout(void);
/**
* @brief Function executed on Radio Rx Error event
*/
static void OnRxError(void);
/* USER CODE BEGIN PFP */
/**
* @brief Function executed on when led timer elapses
* @param context ptr of LED context
*/
/**
* @brief PingPong state machine implementation
*/
static void Concentrator_Process(void);
static uint8_t SumCheck(uint8_t *buf,uint8_t len);
/* USER CODE END PFP */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch (GPIO_Pin)
{
case BUTTON_SW1_PIN:
BSP_LED_Toggle(LED_BLUE) ;
HAL_Delay(20);
BSP_LED_Toggle(LED_BLUE) ;
APP_PRINTF("BUTTON SW1\r\n");
break;
case BUTTON_SW2_PIN:
BSP_LED_Toggle(LED_GREEN) ;
HAL_Delay(20);
BSP_LED_Toggle(LED_GREEN) ;
APP_PRINTF("BUTTON SW2\r\n");
break;
case BUTTON_SW3_PIN:
BSP_LED_Toggle(LED_RED) ;
HAL_Delay(20);
BSP_LED_Toggle(LED_RED) ;
APP_PRINTF("BUTTON SW3\r\n");
break;
default:
APP_PRINTF("Unkonw Button\r\n");
break;
}
}
/* Exported functions ---------------------------------------------------------*/
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));
/* 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 */
/* Radio Set frequency */
Radio.SetChannel(RF_FREQUENCY);
/* Radio configuration */
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);
/* LED initialization*/
BSP_LED_Init(LED_GREEN);
BSP_LED_Init(LED_RED);
BSP_LED_Init(LED_BLUE);
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.Rx(0);
/*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, Concentrator_Process);
/* USER CODE END SubghzApp_Init_2 */
}
/* USER CODE BEGIN EF */
/* USER CODE END EF */
/* Private functions ---------------------------------------------------------*/
static void OnTxDone(void)
{
/* USER CODE BEGIN OnTxDone */
APP_LOG(TS_ON, VLEVEL_L, "OnTxDone\n\r");
/* Update the State of the FSM*/
State = S_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 */
}
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);
BSP_LED_On(LED_GREEN);
BSP_LED_On(LED_RED);
/* Record payload Signal to noise ratio in Lora*/
SnrValue = LoraSnr_FskCfo;
/* Update the State of the FSM*/
State = S_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 < RxBufferSize; 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 */
}
static void OnTxTimeout(void)
{
/* USER CODE BEGIN OnTxTimeout */
APP_LOG(TS_ON, VLEVEL_L, "OnTxTimeout\n\r");
/* Update the State of the FSM*/
State = S_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 */
}
static void OnRxTimeout(void)
{
/* USER CODE BEGIN OnRxTimeout */
APP_LOG(TS_ON, VLEVEL_L, "OnRxTimeout\n\r");
/* Update the State of the FSM*/
State = S_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 */
}
static void OnRxError(void)
{
/* USER CODE BEGIN OnRxError */
APP_LOG(TS_ON, VLEVEL_L, "OnRxError\n\r");
/* Update the State of the FSM*/
State = S_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 */
}
/* USER CODE BEGIN PrFD */
static void Concentrator_Process(void)
{
uint16_t SenID = 0;
uint16_t ConID = 0;
int16_t temp_v = 0;
uint16_t bat_v = 0;
uint16_t pa11_v = 0;
Radio.Rx(0);
switch (State)
{
//接收处理
case S_RX:
APP_LOG(TS_ON, VLEVEL_L, "payload. size=%d \n\r", RxBufferSize);
for (int i = 0; i < RxBufferSize; i++)
{
APP_LOG(TS_OFF, VLEVEL_L, "%02X", BufferRx[i]);
if (i % 16 == 15)
{
APP_LOG(TS_OFF, VLEVEL_L, "\n\r");
}
}
APP_LOG(TS_OFF, VLEVEL_L, "\n\r");
//判断长度
if(BufferRx[2] == RxBufferSize)
{
//判断帧头
if((BufferRx[0] == 0xA5) && (BufferRx[1] == 0x7E))
{
//判断SUM
if(BufferRx[RxBufferSize - 1] == SumCheck(BufferRx,RxBufferSize - 1))
{
//判断地址是否正确
memcpy(&ConID,&BufferRx[3],2);
if((ConID == Concentrator_ID) || (ConID == 0xFFFF))
{
//传感器类型
if(BufferRx[7] == DATA_TYPE01)
{
//获取传感器ID
memcpy(&SenID,&BufferRx[5],2);
//获取温度
memcpy(&temp_v,&BufferRx[8],2);
//获取BAT电压
memcpy(&bat_v,&BufferRx[10],2);
//获取PA11的ADC
memcpy(&pa11_v,&BufferRx[12],2);
APP_LOG(TS_ON, VLEVEL_L, "Recv:ID=0x%04x Temperature = %d.%d Battery = %d PA11Value = %d\n\r",SenID,temp_v>>8,(uint8_t)temp_v,bat_v,pa11_v);
}
/*
//其他类型的解析
else if
{
}
*/
else
{
APP_LOG(TS_OFF, VLEVEL_M, "Unknown type\n\r");
}
}
else
{
APP_LOG(TS_OFF, VLEVEL_M, "The desired ID =%d ,ID = %d error\n\r",Concentrator_ID,ConID);
}
}
else
{
APP_LOG(TS_OFF, VLEVEL_M, "Checksum error\n\r");
}
}
else
{
APP_LOG(TS_OFF, VLEVEL_M, "The data header is incorrect\n\r");
}
}
else
{
APP_LOG(TS_OFF, VLEVEL_M, "Packet length error\n\r");
}
BSP_LED_Off(LED_GREEN);
BSP_LED_Off(LED_RED);
State = S_IDLE;
break;
case S_IDLE:
case S_TX:
case S_RX_TIMEOUT:
case S_RX_ERROR:
case S_TX_TIMEOUT:
State = S_IDLE;
break;
default:
break;
}
}
static uint8_t SumCheck(uint8_t *buf,uint8_t len)
{
uint8_t i = 0;
uint8_t sum = 0;
for(i = 0;i < len;i++)
{
sum += buf[i];
}
return sum;
}
将PA11首先接电源,然后接地。执行效果如下:
左边为采集端右边为接收端。
五、 小结
通过这个ADC定时采集并过LoRa进行上报的例子,便于理解基于STM32WLE5的传感器开发过程。实际应用情况比这复杂的多,还需考虑传感器较多时碰撞规避,集中器对传感器的数据下发或者配置等等。当然也包括稳健性的考量需要启动看门狗,做一些异常处理等等。
文件中提供的易智联Demo板链接如下:
版权声明:本文为CSDN博主「ww2801」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ww2801/article/details/122583995
暂无评论