Entrar en el modo de parada en STM32L011 solo funciona la primera vez

2

Tengo una aplicación muy típica aquí: largos períodos de consumo de corriente mínimo (15 minutos a 24 horas de sueño) con cortos períodos de actividad entre ellos. En mi caso particular, esto se debe principalmente a la manipulación de ADC y GPIO (lo que explica parte de la inicialización adicional en mi código que no se usa), pero para este ejemplo, solo trato de lograr lo siguiente:

  1. Encienda el LED
  2. Delay 1000ms (usando el retardo de HAL, no importa la eficiencia aquí)
  3. apagar el LED
  4. entrar en modo de parada
  5. Haga que el temporizador de activación de RTC me devuelva al paso 1. después de 5 segundos

Hasta ahora, esto funciona bien para el primer bucle: veo que el LED se enciende, se apaga después de 1000 ms, permanece apagado durante 5 segundos (el consumo de corriente cae a aproximadamente 2uA) y luego se enciende nuevamente. Sin embargo, el LED nunca se apaga de nuevo. No puedo usar SWD para depurar esto, ya que parece que la placa nunca ingresa al modo de parada con la depuración de SWD activa.

Mi código (parcialmente generado por STM32CubeMX y parcialmente por leer documentos) es el siguiente:

#include "main.h"
#include "stm32l0xx_hal.h" // specific target MCU is STM32L011D3P6

ADC_HandleTypeDef hadc;

RTC_HandleTypeDef hrtc;

void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);
static void MX_ADC_Init(void);
static void MX_RTC_Init(void);

void SystemPower_Config(void);

int main(void)
{
  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_ADC_Init();
  MX_RTC_Init();

  SystemPower_Config();

  #ifdef DEBUG
    HAL_DBGMCU_EnableDBGStopMode();
  #else
    HAL_DBGMCU_DisableDBGStopMode();
  #endif

  while (1)
  {
    HAL_GPIO_WritePin(LED_1_GPIO_Port, LED_1_Pin, GPIO_PIN_SET);
    HAL_Delay(1000);
    HAL_GPIO_WritePin(LED_1_GPIO_Port, LED_1_Pin, GPIO_PIN_RESET);

    HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);

    if (HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 5, RTC_WAKEUPCLOCK_CK_SPRE_16BITS) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_RCC_PWR_CLK_ENABLE();
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

  }  
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_PeriphCLKInitTypeDef PeriphClkInit;

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI
                              |RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_DIV4;
  RCC_OscInitStruct.HSICalibrationValue = 16;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.MSICalibrationValue = 0;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_4;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }

  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }

  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

static void MX_ADC_Init(void)
{

  ADC_ChannelConfTypeDef sConfig;

  hadc.Instance = ADC1;
  hadc.Init.OversamplingMode = DISABLE;
  hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV16;
  hadc.Init.Resolution = ADC_RESOLUTION_12B;
  hadc.Init.SamplingTime = ADC_SAMPLETIME_160CYCLES_5;
  hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
  hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc.Init.ContinuousConvMode = DISABLE;
  hadc.Init.DiscontinuousConvMode = DISABLE;
  hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc.Init.DMAContinuousRequests = DISABLE;
  hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc.Init.LowPowerAutoWait = DISABLE;
  hadc.Init.LowPowerFrequencyMode = ENABLE;
  hadc.Init.LowPowerAutoPowerOff = ENABLE;
  if (HAL_ADC_Init(&hadc) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_4;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
  if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

static void MX_RTC_Init(void)
{

  RTC_TimeTypeDef sTime;
  RTC_DateTypeDef sDate;

  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  hrtc.Init.AsynchPrediv = 127;
  hrtc.Init.SynchPrediv = 255;
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }

  sTime.Hours = 0x0;
  sTime.Minutes = 0x0;
  sTime.Seconds = 0x0;
  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }

  sDate.WeekDay = RTC_WEEKDAY_MONDAY;
  sDate.Month = RTC_MONTH_JANUARY;
  sDate.Date = 0x1;
  sDate.Year = 0x0;

  if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  HAL_GPIO_WritePin(SENSOR_EN_GPIO_Port, SENSOR_EN_Pin, GPIO_PIN_RESET);

  HAL_GPIO_WritePin(GPIOA, LED_2_Pin|LED_1_Pin|LED_0_Pin, GPIO_PIN_RESET);

  GPIO_InitStruct.Pin = SELF_TEST_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(SELF_TEST_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = SENSOR_EN_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(SENSOR_EN_GPIO_Port, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = LED_2_Pin|LED_1_Pin|LED_0_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}


void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc){
  __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
}

void SystemPower_Config(void)
{
  __HAL_RCC_PWR_CLK_ENABLE();
  HAL_PWREx_EnableUltraLowPower();
}

void Error_Handler(void)
{
  while(1) 
  {
  } 
}

Main.h simplemente asigna los pines GPIO a los nombres utilizados en el código; me alegro de publicar eso si es útil. ¿Alguien puede señalar dónde me voy mal aquí? ¿O sugerir un enfoque de depuración útil? (dado que lamentablemente no puedo hacer mucho registrándome con SWD arruinando mi capacidad de entrar en modo de suspensión)

    
pregunta stefandz

1 respuesta

4

La respuesta se identificó en el foro STM32 - ¡días felices!

No pude implementar y habilitar la interrupción RTC correctamente. Esto requirió los siguientes pasos:

  1. En stm32l0xx_hal_msp.c agregue lo siguiente a la función HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc) :

    HAL_NVIC_SetPriority(RTC_IRQn, 0x0, 0);
    
    HAL_NVIC_EnableIRQ(RTC_IRQn);
    
  2. En stm32l0xx_it.c agregue las siguientes líneas:

    extern RTC_HandleTypeDef hrtc;
    
    void RTC_IRQHandler(void)
    {
      HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
    }
    

Sin esto, los indicadores que se establecen después de los incendios de interrupción nunca se borran, por lo que funciona la primera vez, y no después. Gracias a @Bence Kaulics por su ayuda en esto también.

    
respondido por el stefandz

Lea otras preguntas en las etiquetas