【STM32WLE5之lora:5、易智联LM40评估板ADC定时采集上报】
时间:2023-09-08 09:37:01
STM32WLE5之lora:5、易智联LM40评估板ADC定期收集和报告
- 前言
- 一、数据报告格式
- 二、 ADC采集
-
- 1、ADC的初始化
- 2、ADC采集
- 三、 收集端报告处理流程
- 四、 接收端接收处理流程
- 五、 小结
- 文件中提供的易智联Demo板链接如下:
前言
本节我们在PingPong在例程的基础上完成STM32WLE5内部温度,VBAT电压及外部ADC定期收集和报告。STM32WlE5的ADC采样、软件定时和Lora发送和接收私有协议的方法使用方便STM32WLE与传统串口结构Lora点对点类似于模块,点对多点采集报告应用。
由于STM32WLE5集成的是Semtech的SX内核为1262,频点一致,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
#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