Микроконтроллеры STM32 в настоящее время являются одними из самых популярных, из многих моделей я осмелюсь сказать, что STM32F103C8, вероятно, является одним из самых известных микроконтроллеров от ST, в основном из-за его включения в плату Blue Pill, которая чрезвычайно популярна в России. пространство производителя.

Разработка «голого железа» на микроконтроллере ST упрощается благодаря уровню аппаратной абстракции (HAL), предоставляемому производителем, который, как следует из названия, позволяет абстрагировать аппаратные операции для функций API, которые должны быть одинаковыми независимо от конкретного устройства. Эта абстракция упрощает и ускоряет разработку, обратная сторона этого заключается в том, что операции с регистрами скрыты, хотя они по-прежнему доступны и с ними можно работать.

Мое длинное введение по этим темам сделано для того, чтобы понять, почему я пишу это; хотя STM32F103 чрезвычайно популярен, он обычно не используется в операциях с низким энергопотреблением, а сообщений и примеров о том, как работать с этим конкретным устройством, немного. Моя попытка состоит в том, чтобы дать несколько советов о том, как работать с режимом STOP в устройстве и использовать RTC Alarms для периодического пробуждения.

Создание проекта STM32Cube выходит за рамки проекта, однако я хочу отметить, что для того, чтобы настроить конфигурацию RTC, параметр RTC OUT должен быть установлен на «Выход RTC на выводе тампера». », этот параметр включает настройку будильника. Использование STM32Cube MX — это самый простой способ настроить проект, хотя позже можно изменить конфигурацию при редактировании кода. На следующем рисунке показано, как настроить STM32Cube MX для RTC.

Что мы в основном ищем в настройке RTC, так это то, что выход установлен как RTC_OUTPUTSOURCE_ALARM, что позволяет работать будильнику, нам это понадобится в качестве источника пробуждения после того, как мы настроим режим низкого энергопотребления. Функция инициализации, используемая в стандартном проекте STM32Cube, созданном STM32Cube MX, показана ниже.

void MX_RTC_Init(void)
{
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef DateToUpdate = {0};
RTC_AlarmTypeDef sAlarm = {0};
/** Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN Check_RTC_BKUP */
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 23;
sTime.Minutes = 59;
sTime.Seconds = 30;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;
DateToUpdate.Month = RTC_MONTH_JANUARY;
DateToUpdate.Date = 0x1;
DateToUpdate.Year = 0x0;
if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
/** Enable the Alarm A
*/
sAlarm.AlarmTime.Hours = 0;
sAlarm.AlarmTime.Minutes = 0;
sAlarm.AlarmTime.Seconds = 20;
sAlarm.Alarm = RTC_ALARM_A;
if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
}

Помимо hrtc.Init.Output = RTC_OUTPUTSOURCE_ALARM, другой важной вещью, которую следует отметить, является созданная структура sAlarm, которая будет обрабатывать всю конфигурацию сигналов тревоги. Наконец, функция HAL_RTC_SetAlarm_IT необходима, чтобы сообщить микроконтроллеру, что мы будем использовать прерывание, когда значения времени RTC совпадают со значениями времени тревоги. Еще раз важно отметить, что для этого должны быть включены прерывания RTC (тревога работает без прерываний, но для источника пробуждения с низким энергопотреблением прерывания необходимы).

Прежде чем перейти к обработке аварийного прерывания и к тому, как делать периодические аварийные прерывания, я хочу поговорить о режимах с низким энергопотреблением. Все микроконтроллеры STM32 имеют 3 режима работы с низким энергопотреблением, разница между ними заключается в том, что остается включенным, а что можно использовать в качестве источника пробуждения. В следующей таблице, взятой из таблицы данных, показаны различные режимы с низким энергопотреблением и их различия.

Сверху вниз каждый режим с низким энергопотреблением потребляет меньше энергии, при этом спящий режим является наиболее энергоемким, а режим ожидания — наиболее энергоэффективным. В спящем режиме все часы, кроме часов процессора, включены, что означает, что любой таймер может использоваться в качестве источника пробуждения, в двух других режимах это не так, что означает, что для периодического пробуждения необходимо использовать какой-то другой метод. RTC имеет другой источник тактовой частоты, на который не влияют режимы пониженного энергопотребления, что делает его идеальным кандидатом для наших целей. В дополнение к этому, все режимы с низким энергопотреблением могут быть активированы любым прерыванием с помощью параметра WFI при настройке режима с низким энергопотреблением.

В следующем фрагменте показано, как запустить режим пониженного энергопотребления, важно отметить, что перед входом в режим пониженного энергопотребления (режим остановки в нашем случае) нам необходимо приостановить счетчик тиков на микроконтроллере, если мы этого не сделаем, он Возможно, мы выйдем из режима пониженного энергопотребления раньше, чем захотим. Также следует отметить, что вторым аргументом HAL_PWR_EnterSTOPMode является форма пробуждения, которая в данном случае является WFI, что означает, что любое прерывание будет пробуждать устройство.

HAL_SuspendTick();
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

При выходе из режима остановки нам нужно настроить системные часы и возобновить тики, следующий фрагмент кода показывает, как это можно сделать (обратите внимание, что все это выполняется в HAL).

SystemClock_Config();
HAL_ResumeTick();

После всех настроек и строк для входа в режим питания сосредоточим внимание на будильнике. Здесь следует отметить две важные вещи: поскольку мы используем прерывания для оповещения, необходим обратный вызов. При использовании STM32Cube мы обрабатываем это с помощью функции

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
}

Мы можем поместить что угодно внутрь этой функции, и она будет вызываться, когда таймер RTC достигнет значений, которые мы поместили в структуру будильника. Значения в структуре AlarmTime, которые размещаются, соответствуют ожидаемому времени срабатывания будильника. Если мы намерены просто установить определенное время каждый день, когда должен срабатывать будильник, достаточно конфигурации при инициализации функции, однако, если мы намерены настроить периодическое пробуждение, нам нужно учитывать текущее время включения. RTC и добавьте количество времени в будущем, которое мы хотим, чтобы будильник сработал. Следующий фрагмент кода настраивает новый будильник через 20 секунд.

HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN);
time.Seconds += 20;
if(time.Seconds>=60)
{
time.Minutes++;
time.Seconds= time.Seconds -60;
}
if(time.Minutes >= 60)
{
time.Hours++;
time.Minutes = time.Minutes - 60;
}
if(time.Hours > 23)
{
time.Hours = 0;
}
sAlarm.Alarm = RTC_ALARM_A;
sAlarm.AlarmTime.Hours = time.Hours;
sAlarm.AlarmTime.Minutes = time.Minutes;
sAlarm.AlarmTime.Seconds = time.Seconds;
HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);

Сначала мы получаем текущее время, для того, чтобы получить текущее время нам также нужно прочитать дату, иначе время не будет обновляться. После этого мы обрабатываем обновление, так как элементы структуры RTC_HandleTypeDef являются типами данных uint8_t, которые могут превышать значение 59, нам нужно убедиться, что любое превышение секунд, минут или часов должно переноситься и переноситься на следующую единицу времени. . Наконец, когда мы все рассчитали, мы еще раз настраиваем будильник, чтобы он срабатывал в новое время. Этот код может быть помещен внутри функции и вызываться при обратном вызове прерывания тревоги для периодического выхода из режима остановки.

Подводя итог, скажу, что несмотря на то, что на STM32F103 можно выполнять операции с низким энергопотреблением, это одно из самых энергоемких устройств STM32, поэтому лучше использовать другие устройства, более подходящие для этих задач. При этом, если нет другого выбора устройства, возможна работа с низким энергопотреблением на STM32F103, особенно если используется режим остановки. Позже я обновлю историю репозиторием github, показав полный рабочий пример для более наглядного примера. Я надеюсь, что это будет полезно для всех, кто работает с этим конкретным случаем применения.