锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

【STM32WLE5之lora:5、易智联LM40评估板ADC定时采集上报】

时间:2023-09-08 09:37:01 集成电路lm3724em5

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 
锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章