Aquí tengo una aplicación muy típica: largos períodos de consumo mínimo de corriente (15 minutos a 24 horas de duración del sueño) con cortos períodos de actividad en el medio. En mi caso particular, esto es principalmente 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 estoy tratando de lograr lo siguiente:
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 2 uA) y luego se vuelve a encender. 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 a partir de la lectura de 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 pines GPIO a los nombres utilizados en el código; feliz de publicarlo si es útil. ¿Alguien puede señalar dónde me estoy equivocando aquí? ¿O sugerir un enfoque de depuración útil? (dado que, lamentablemente, no puedo registrarme mucho con SWD, lo que arruina mi capacidad para entrar en modo de suspensión)
La respuesta se identificó en el foro STM32 : ¡días felices!
Estaba fallando al implementar y habilitar la interrupción RTC correctamente. Esto requirió los siguientes pasos:
Agregue stm32l0xx_hal_msp.c
lo siguiente a la HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc)
función:
HAL_NVIC_SetPriority(RTC_IRQn, 0x0, 0);
HAL_NVIC_EnableIRQ(RTC_IRQn);
Además, stm32l0xx_it.c
agregue las siguientes líneas:
extern RTC_HandleTypeDef hrtc;
void RTC_IRQHandler(void)
{
HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
}
Sin esto, las banderas que se configuran después de que se dispara la 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.
Bence Kaulics
stefandz
Bence Kaulics
stefandz
Bence Kaulics
PWR_FLAG_WU
, estaba en un núcleo M0. No es el caso aquí. Compruebe si hay algunas banderas adicionales en el caso de M0+.