Страница на этапе разработки
В данном уроке мы рассмотрим режимы сна для микроконтроллеров фирмы STM32.
Факторы, влияющие на режимы сна:
Рассмотрим один из примеров применения режима сна в микроконтроллере STM32G031F4P6. В данном микроконтроллере есть несколько режимов сна (не все режимы сна можно реализовать на том или ином микроконтроллере), из доступных нас будет интересовать режим STOP1 (режим STOP2 для более глубокова сна в данном микроконтроллере не доступен).
и пока оставим все остальные настройки без изменения.
Для проверки его работы мы будем мигать светодиодом, который подключен к ножке PA4. Рабочую частоту микроконтроллера выберем 8МГц (тактирование от внутреннего источника), а тактирование LPTIM1 таймера от LSI:
Программный код довольно простой, но он нам полностью подойдет для понимания экономии в потреблении. Ток потребления у Вас будет зависит от резистора который идет на светодиод и от самого светодиода. У меня ток составил около 4 мА.
Теперь рассмотрим второй программный код:
/* USER CODE BEGIN 0 */
void Sleep_ms(uint32_t ms){
uint32_t start = HAL_GetTick();
while ((HAL_GetTick() — start) < ms) {
// Входим в режим сна CPU (Sleep)
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
}
HAL_ResumeTick();// восстанавливаем HAL tick
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_Delay(2000);
/* USER CODE END 2 */
Прежде чем запустить программный код, проверьте, что у Вас в коде стоит вначале небольшая задержка, это нужно для того, что бы можно было подключаться к микроконтроллеру и записывать новый код. Иначе микроконтроллер заснет, и вы не можете к нему подключиться и Вам придется ловить момент когда он не спит, что бы перезалить программный код.
Сам программный код довольно прост, мы указываем на какое время нам нужно лечь спать, отключаем все что можно безопасно отключить, спим, по истечению времени наш таймер будит микроконтроллер и программа продолжается дальше с того места где остановилась.
Единственно, что остается под вопросом это делитель:Мы его должны выставить так, что бы не зависимости от выбранного времени у нас счетчик не переполнялся и при этом точно считал.
Так же нужно помнить про источник тактирования:
LSI (~32.768 kHz RC) → точность ±1–2%.
LSE (32768 Hz кварц) → высокая точность (рекомендуется для RTC/таймера).
Что бы нам не просчитывать самим необходимый делитель, мы расширим свою программу:
И теперь текже замеряем ток потребления, он у меня составил немного меньше 3мА. Как мы видим потребление снизилось на 1мА в среднем.
Для использования полноценного STOP1, необходимо немного доработать рабочие файлы.
Для начало надо активировать прерывание на таймере LPTIM1 и скомпилировать файл.
Дале найти файл stm32g0xx_it.c и в конце добавить функцию:
/* USER CODE BEGIN 1 */
void HAL_LPTIM_AutoReloadMatchCallback(LPTIM_HandleTypeDef *hlptim){
if (hlptim->Instance == LPTIM1){
HAL_LPTIM_Counter_Stop_IT(hlptim);
}
}
/* USER CODE END 1 */
Далее в теле программы добавить подпрограмму:
extern LPTIM_HandleTypeDef hlptim1;
void Sleep_Stop1_ms(uint32_t ms){
// 1 тик LPTIM ≈ 30.5 мкс (при LSI = 32768 Гц, PSC = 1)
uint32_t ticks = (ms * 32768UL) / 1000UL;
if (ticks < 1) ticks = 1; // минимум 1 тик
if (ticks > 0xFFFF) ticks = 0xFFFF;
// Сбрасываем счётчик
__HAL_LPTIM_DISABLE(&hlptim1);
__HAL_LPTIM_ENABLE(&hlptim1);
// Загружаем ARR = ticks
__HAL_LPTIM_AUTORELOAD_SET(&hlptim1, ticks);
// Разрешаем прерывание по совпадению
__HAL_LPTIM_CLEAR_FLAG(&hlptim1, LPTIM_FLAG_ARRM);
__HAL_LPTIM_ENABLE_IT(&hlptim1, LPTIM_IT_ARRM);
// Запускаем счётчик
HAL_LPTIM_Counter_Start_IT(&hlptim1, ticks);
// Уходим в STOP1
HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
// После пробуждения MCU продолжит отсюда
HAL_LPTIM_Counter_Stop_IT(&hlptim1);
}
Использование функции:
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_6);
Sleep_Stop1_ms(4); // сон ровно 4 мс
Теперь рассмотрим режимы сна, в микроконтроллере stm32f030, в нем нет LPTIM таймера, но зато есть другие возможности переходить в режим сна.
Доступные режимы сна у STM32F030
Sleep mode
CPU спит, периферия работает (TIM, UART, I²C).
Ток: ~1–2 мА.
Выход: любое прерывание.
Stop mode
CPU и большинство тактировок выключены.
RAM сохраняется.
Ток: ~5–10 µA.
Выход: по EXTI или RTC (обычные TIM тут не работают).
Standby
Всё выключено, RAM теряется.
Ток: ~1 µA.
Выход: только по Reset/RTC/WKUP.
Реализуем сон используя таймер. В качестве таймера возьмем TIM2.
Настройка параметров:
В CubeMX включаем TIM2, Internal Clock.
Prescaler = (SystemCoreClock / 1000) — 1 → таймер считает в миллисекундах.
ARR = нужное количество мс (например, 4).
Включить Update Interrupt.
Программный код:
__HAL_TIM_SET_AUTORELOAD(&htim2, ms);
__HAL_TIM_SET_COUNTER(&htim2, 0);
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
__HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE);
HAL_TIM_Base_Start_IT(&htim2);
// Уходим в Sleep
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); HAL_TIM_Base_Stop_IT(&htim2);
}
В обработчике прерывания пишем:
void TIM2_IRQHandler(void){
HAL_TIM_IRQHandler(&htim2);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
if (htim->Instance == TIM2){
// факт пробуждения
}
}
микроконтроллер спит 4 мс и пробуждается по таймеру.
Если нам нужно спать секунды, код необходимо немного переделать:
Для длинного сна (секунды, минуты) → включаем RTC и уходим в Stop mode.
// Пример: сон на 1 секунду через RTC wake-up
void Sleep_Stop(uint32_t seconds){
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, seconds, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI);
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
}
Как мы видим для STM32F030 короткие задержки (мс → десятки мс) → Sleep + TIM2.
Для длинных (секунды, минуты) → Stop + RTC wake-up.
Подготовка проекта в CubeMX:
Включаем TIM2 → Clock Source = Internal Clock.
Включаем прерывание Update Event (UIF).
NVIC → включаем TIM2_IRQn.
Код генерации → HAL.
Программный код:
static volatile uint8_t tim2_wakeup = 0;
void Sleep_ms_F0(uint32_t ms){
tim2_wakeup = 0;
// Настроить автоперезагрузку под миллисекунды
// Предполагаем, что в CubeMX PSC выставлен так, чтобы 1 тик = 1 мс
__HAL_TIM_SET_AUTORELOAD(&htim2, ms);
__HAL_TIM_SET_COUNTER(&htim2, 0);
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
// Разрешаем прерывание
__HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE);
HAL_TIM_Base_Start_IT(&htim2);
// Уходим в Sleep до прерывания
while (!tim2_wakeup){
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
}
HAL_TIM_Base_Stop_IT(&htim2);
}
Так же прописываем прерывания:
void TIM2_IRQHandler(void){
HAL_TIM_IRQHandler(&htim2);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
if (htim->Instance == TIM2){
tim2_wakeup = 1; // флаг для выхода из сна
}
}
Пример использования:
while (1){
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_6);
Sleep_ms_F0(4); // сон ровно 4 мс
}
Как это работает:
Таймер TIM2 настроен так, что 1 тик = 1 мс
(Prescaler = (SystemCoreClock/1000)-1, ARR = ms).
микроконтроллер уходит в Sleep (WFI), ток падает, но TIM2 продолжает работать.
Через ms мс TIM2 даёт прерывание → MCU просыпается → флаг tim2_wakeup = 1.
Теперь напишем код для STM32F030, где микроконтроллер будет засыпать в STOP mode на секунды/минуты и просыпаться от RTC Wake-Up таймера.
Настройка проекта в CubeMX:
Включаем RTC.
Clock Source → LSI (32 kHz) или LSE (32.768 kHz кварц).
Включаем опцию Activate Clock Source for RTC.
В RTC настрой WakeUp Timer (можно оставить выключенным, мы включим его в коде).
В NVIC включаем RTC_IRQn.
Программный код:
extern RTC_HandleTypeDef hrtc;
void Sleep_Stop_RTC(uint32_t seconds){
// Отключаем старый wake-up
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
// Устанавливаем новый wake-up таймер
// Таймер работает от 1 Hz при LSI/LSE → счётчик = секунды
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, seconds, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
// Уходим в STOP mode, ждём прерывания
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI);
// MCU проснулся → выключаем wake-up таймер
HAL_RTCEx_DeactivateWakeUpTimer(&hrtc);
// После STOP режимов надо восстановить тактирование
SystemClock_Config();
}
Прописываем прерывание:
while (1){
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_6); // мигнули светодиодом
Sleep_Stop_RTC(5); // спим 5 секунд в STOP mode
}
Нюансы использования:
STOP mode = ток ~5–10 µA.
RAM сохраняется, массивы и переменные не теряются.
После выхода из STOP нужно перезапустить тактирование (через SystemClock_Config()).
Максимальное время зависит от RTC (до 18 бит → 65535 секунд ≈ 18 часов).