2
\$\begingroup\$

I have a very typical application here - long periods of minimal current consumption (15 minutes to 24 hours sleep duration) with short periods of activity in between. In my particular case, this is mostly ADC and GPIO manipulation (which explains some of the extra initialisation in my code which is unused) but for this example, I am just trying to achieve the following:

  1. Turn LED on
  2. Delay 1000ms (using HAL delay - don't care about efficiency here)
  3. Turn LED off
  4. Enter Stop mode
  5. Have RTC Wakeup Timer return me to Step 1. after 5 seconds

So far this works well for the first loop through - I see the LED turn on, turn off after 1000ms, stay off for 5 seconds (current draw drops to approx. 2uA) and then turn on again. However, the LED never turns off again. I can't use SWD to debug this as the board seems to never enter Stop mode with SWD debugging active.

My code (partially generated by STM32CubeMX and partially from reading docs) is as below:

#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 simply maps GPIO pins to the names used in the code - happy to post that if it's useful. Can anyone point out where I am going wrong here? Or suggest a helpful debug approach? (given that I sadly can't do much register poking with SWD ruining my ability to go into sleep mode)

\$\endgroup\$
  • \$\begingroup\$ Tough question cannot give much help. Can you toggle another LED in the RTC callback? And instead of the LED on, 1 sec delay, LED off; make that LED blink a couple of times before entering STOP mode. Maybe the MCU wakes up at once after entering STOP so you cannot detect the LED off state, I do not know. \$\endgroup\$ – Bence Kaulics Jan 19 '17 at 21:48
  • 1
    \$\begingroup\$ Hi @BenceKaulics - thanks for your thoughts. I went a slightly different route - using a current shunt amplifier and an oscilloscope, I checked the current draw over time. It's very easy to see when the device enters stop mode this way (and trigger from this). I can see from the scope trace that the device only enters stop mode once, that is it doesn't enter a second time and then immediately return. \$\endgroup\$ – stefandz Jan 20 '17 at 10:50
  • 1
    \$\begingroup\$ Any possible hard fault, unhandled interrupt? \$\endgroup\$ – Bence Kaulics Jan 20 '17 at 11:20
  • \$\begingroup\$ I don't think so - if I add a shorter blink after the HAL_PWR_EnterSTOPMode() call I see that - and then I just see a continuous long blink, short blink loop, implying that code execution continues and the while(1) loop in main() is getting executed - just that the HAL_PWR_EnterSTOPMode() is only effective the first time. \$\endgroup\$ – stefandz Jan 20 '17 at 12:08
  • 1
    \$\begingroup\$ I had similar problem only with STANDBY mode and when I forgot to clear this flag PWR_FLAG_WU, it was on an M0 core. Not the case here. Check if there are some additional flags in case of the M0+. \$\endgroup\$ – Bence Kaulics Jan 20 '17 at 12:16
4
\$\begingroup\$

The answer was identified on the STM32 forum - happy days!

I was failing to implement and enable the RTC interrupt correctly. This required the following steps:

  1. In stm32l0xx_hal_msp.c add the following to the HAL_RTC_MspInit(RTC_HandleTypeDef* hrtc) function:

    HAL_NVIC_SetPriority(RTC_IRQn, 0x0, 0);
    
    HAL_NVIC_EnableIRQ(RTC_IRQn);
    
  2. In stm32l0xx_it.c add the following lines:

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

Without this, flags that are set after the interrupt fires are never cleared - so it works first time, and not after. Thanks to @Bence Kaulics for his help on this too.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.