Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
2743
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать

Управление питанием

491

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

ные функции, HAL_PWREx_EnableOverDrive() и HAL_PWREx_DisableOverDrive() для выполне-

ния этой операции.

Режим малоинтенсивной работы противоположен режиму высокоинтенсивной работы и

заключается в понижении частоты ЦПУ и отключении некоторых периферийных устройств. В этом режиме можно перевести внутренний регулятор напряжения в режим пониженного энергопотребления. В некоторых микроконтроллерах STM32F4/F7 режим

малоинтенсивной работы доступен даже в режиме останова.

19.3.2.2. Спящий режим

Перейти в спящий режим (sleep mode) можно с помощью выполнения инструкции WFI или WFE. В спящем режиме все выводы I/O сохраняют то же состояние, что и в рабочем режиме. Однако нам не следует заботиться об ассемблерных инструкциях, поскольку CubeHAL предоставляет функцию:

void HAL_PWR_EnterSLEEPMode(uint32_t Regulator, uint8_t SLEEPEntry);

Первый параметр, Regulator, не имеет смысла в спящем режиме для всей серии STM32F и оставлен для совместимости с серией STM32L. Второй параметр, SLEEPEntry, может принимать значения PWR_SLEEPENTRY_WFI или PWR_SLEEPENTRY_WFE: как следует из названий, первый выполняет инструкцию WFI, а второй – WFE.

Если вы посмотрите на функцию HAL_PWR_EnterSLEEPMode(), то обнаружите, что, если мы передадим параметр PWR_SLEEPENTRY_WFE, он последовательно выполнит две инструкции WFE. Это приводит к тому, что HAL_PWR_EnterSLEEPMode() переходит в спящий режим таким же образом, как она вызывается с параметром PWR_SLEEPENTRY_WFI (двойной вызов WFE приводит к тому, что если установлен регистр событий, то он сбрасывается первой инструкцией WFE, а второй переводит микроконтроллер в спящий режим). Я не знаю, почему ST приняла этот подход. Если вы хотите получить полный контроль над тем, как микроконтроллер переводится в режимы пониженного энергопотребления, вам придется изменить содержимое этой функции по своему усмотрению. Ясно, что микроконтроллер выйдет из спящего режима после выполнения условия выхода инструкции WFE.

Если для перехода в спящий режим используется инструкция WFI, то любое прерывание от периферийного устройства, подтвержденное контроллером вложенных векторных прерываний (NVIC), может пробудить устройство из спящего режима.

Если для перехода в спящий режим используется инструкция WFE, то микроконтроллер выходит из спящего режима, как только происходит событие. Событие пробуждения может быть сгенерировано в следующих случаях:

Управление питанием

492

разрешение прерывания в регистре управления периферийным устройством, но не в NVIC, и установка бита SEVONPEND в регистре управления системой – Когда микроконтроллер возобновляет работу из WFE, бит отложенного состояния (pending bit) периферийного прерывания и бит отложенного состояния периферийного канала IRQ контроллера NVIC (в регистре NVIC сброса прерываний) должны быть сброшены;

или конфигурирование внешней или внутренней линии EXTI в режиме события (event mode) – Когда ЦПУ возобновляет работу из WFE, нет необходимости сбрасывать бит отложенного состояния периферийного прерывания или бит отложенного состояния канала IRQ контроллера NVIC, так как бит отложенного состояния, соответствующий линии события не установлен.

Данный режим предлагает самое низкое время пробуждения, поскольку не тратится время на вход/выход из прерывания.

19.3.2.3. Режим останова

Режим останова (stop mode) основан на режиме глубокого сна Cortex-M в сочетании с запретом тактирования периферии. В режиме останова все тактирование в домене питания 1,8 В остановлено, а блок PLL и HSI- и HSE-генераторы отключены. SRAM и содержимое регистров сохраняются. В режиме останова все выводы I/O сохраняют то же состояние, что и в рабочем режиме. Регулятор напряжения может быть сконфигурирован как в нормальном режиме работы, так и в режиме пониженного энергопотребления. Для перевода микроконтроллера в режим останова HAL предоставляет функцию:

HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry);

где параметр Regulator принимает значение PWR_MAINREGULATOR_ON для того, чтобы оставить внутренний регулятор напряжения включенным, или значение PWR_LOWPOWERREGULATOR_ON для того, чтобы перевести его в режим пониженного энергопотребления. Пара-

метр STOPEntry может принимать значения PWR_STOPENTRY_WFI или PWR_STOPENTRY_WFE.

Для перехода в режим останова, все биты отложенного состояния линий EXTI, все биты отложенного состояния прерываний периферийных устройств и флаг будильника RTC Alarm должны быть сброшены. В противном случае процедура перехода в режим останова игнорируется, и выполнение программы продолжается. Если приложению необходимо отключить внешний высокочастотный генератор (HSE) перед переходом в режим останова, сначала должен быть переключен источник системного тактового сигнала на HSI-генератор, а затем сброшен бит HSEON. В противном случае, если перед переходом в режим останова бит HSEON остается равным 1, необходимо включить функцию системы защиты тактирования (CSS) для обнаружения любого отказа внешнего генератора (внешнего тактового сигнала) и избежать сбоя при переходе в режим останова.

Любая линия EXTI, сконфигурированная в режиме прерываний или событий, вынуждает ЦПУ выйти из режима останова, если оно перешло в режим пониженного энергопотребления при помощи инструкции WFI или WFE. Поскольку и HSE-генератор, и блок PLL отключаются перед переходом в режим останова, при выходе из этого режима источником тактового сигнала микроконтроллера устанавливается HSI-генератор. Это означает, что наш код должен переконфигурировать схему тактирования в соответствии с желаемым тактовым сигналом SYSCLK.

Управление питанием

493

19.3.2.4. Режим ожидания

Режим ожидания (standby mode) позволяет достичь минимального энергопотребления. Он основан на режиме глубокого сна Cortex-M с отключенным регулятором напряжения. Следовательно, отключается домен питания 1,8-1,2 В. Также отключаются мультиплексор блока PLL, HSI- и HSE-генераторы. Содержимое SRAM и регистров теряется, за исключением регистров цепи автономной работы (standby circuitry). Для перевода микроконтроллера в режим ожидания HAL предоставляет функцию:

void HAL_PWR_EnterSTANDBYMode(void);

Микроконтроллер выходит из режима ожидания при возникновении внешнего сброса (вывод NRST), сброса от IWDG, нарастающего фронта на одном из разрешенных выводов WKUPx или от события RTC. Все регистры сбрасываются после выхода из режима

ожидания, за исключением регистра управления/состояния питания (PWR->CSR). После вы-

хода из режима ожидания выполнение программы возобновляется так же, как и после сброса (выборка вывода начальной загрузки, загрузка байтов конфигурации, выбор вектора сброса и т. д.). Используя макрос:

__HAL_PWR_GET_FLAG(PWR_FLAG_SB);

мы можем проверить, сбрасывается ли микроконтроллер при выходе из режима ожидания. Поскольку и HSE-генератор, и блок PLL отключаются перед переходом в режим ожидания, при выходе из этого режима источником тактового сигнала микроконтроллера установлен HSI-генератор. Это означает, что наш код должен переконфигурировать схему тактирования в соответствии с желаемым тактовым сигналом SYSCLK.

Прочитайте внимательно

Некоторые микроконтроллеры STM32 имеют аппаратную ошибку, которая не позволяет перейти или выйти из режима ожидания. Должны быть выполнены особые условия, прежде чем мы перейдем в этот режим. Обратитесь к перечню аппаратных ошибок (errata sheet) вашего микроконтроллера для получения дополнительной информации об этом.

19.3.2.5. Пример работы в режимах пониженного энергопотребления

В следующем примере, предназначенном для работы на Nucleo-F030R815, показано, как работают режимы пониженного энергопотребления.

Имя файла: src/main-ex1.c

14int main(void) {

15char msg[20];

17HAL_Init();

18Nucleo_BSP_Init();

20/* Прежде чем мы сможем получить доступ к каждому регистру PWR, мы должны включить его */

15 Для других плат Nucleo обратитесь к примерам книги.

Управление питанием

494

21 __HAL_RCC_PWR_CLK_ENABLE(); 22

23while (1) {

24if(__HAL_PWR_GET_FLAG(PWR_FLAG_SB)) {

25/* Если установлен флаг режима ожидания в PWR->CSR, то генерируется сброс

26* при выходе из режима ожидания (STANDBY) */

27sprintf(msg, "RESET after STANDBY mode\r\n");

28HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

29/* Мы должны явно сбросить флаг */

30__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU|PWR_FLAG_SB);

31}

32

33sprintf(msg, "MCU in run mode\r\n");

34HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

35while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET) {

36HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);

37HAL_Delay(100);

38}

39

40 HAL_Delay(200);

41

42sprintf(msg, "Entering in SLEEP mode\r\n");

43HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

44

45 SleepMode();

46

47sprintf(msg, "Exiting from SLEEP mode\r\n");

48HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

49

50while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET);

51HAL_Delay(200);

52

53sprintf(msg, "Entering in STOP mode\r\n");

54HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

55

56 StopMode();

57

58sprintf(msg, "Exiting from STOP mode\r\n");

59HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

60

61while(HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET);

62HAL_Delay(200);

63

64sprintf(msg, "Entering in STANDBY mode\r\n");

65HAL_UART_Transmit(&huart2, (uint8_t*)msg, strlen(msg), HAL_MAX_DELAY);

66

67 StandbyMode();

68

69while(1); // Сюда никогда не придем, так как МК сбрасывается при выходе из STANDBY

70}

Управление питанием

495

71 }

72

73

74void SleepMode(void)

75{

76GPIO_InitTypeDef GPIO_InitStruct;

78/* Отключение всех GPIO для уменьшения энергопотребления */

79 MX_GPIO_Deinit();

80

81/* Конфигурация пользовательской кнопки в качестве генератора внешнего прерывания */

82__HAL_RCC_GPIOC_CLK_ENABLE();

83GPIO_InitStruct.Pin = B1_Pin;

84GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;

85GPIO_InitStruct.Pull = GPIO_NOPULL;

86HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

87

88 HAL_UART_DeInit(&huart2);

89

90/* Приостановить отсчет тиков для предотвращения пробуждения по прерыванию от Systick.

91В противном случае прерывание от Systick пробудит устройство через 1 мс. */

92HAL_SuspendTick();

93

94__HAL_RCC_PWR_CLK_ENABLE();

95/* Запрос на переход в спящий режим (SLEEP) */

96HAL_PWR_EnterSLEEPMode(0, PWR_SLEEPENTRY_WFI);

98/* Возобновление отсчета тиков, если он был приостановлен до перехода в спящий режим */

99 HAL_ResumeTick();

100

101/* Переинициализация выводов GPIO */

102MX_GPIO_Init();

103

104/* Переинициализация UART2 */

105MX_USART2_UART_Init();

106}

Макрос __HAL_RCC_PWR_CLK_ENABLE() в строке 21 включает периферийное устройство PWR: прежде чем мы сможем выполнить какую-либо операцию, связанную с управлением питанием, нам необходимо включить периферийное устройство PWR, даже если мы просто проверяем, установлен ли флаг режима ожидания в регистре PWR->CSR. Это источник причинения уймы головной боли у начинающих пользователей, борющихся с управлением питанием.

Строки [24:31] проверяют, установлен ли флаг режима ожидания: если это так, то это означает, что микроконтроллер был сброшен после выхода из режима ожидания. Строки [33:38] представляют собой рабочий режим: светодиод LD2 мигает, пока мы не нажмем пользовательскую кнопку платы Nucleo, подключенную к выводу PC13. Оставшиеся строки кода в main() просто переключаются между тремя режимами пониженного энергопотребления при каждом нажатии пользовательской кнопки.

Управление питанием

496

Строки [74:106] определяют функцию SleepMode(), используемую для перевода микроконтроллера в спящий режим. Все GPIO сконфигурированы как аналоговые, чтобы уменьшить потребление тока на неиспользуемых I/O (особенно те выводы, которые могут быть источником утечек). Соответствующие им периферийные тактовые сигналы отключены, за исключением периферийного устройства GPIOC: вывод PC13 используется для выхода из режимов пониженного энергопотребления. То же самое относится к интерфейсу UART2 и таймеру SysTick, который останавливается для предотвращения выхода микроконтроллера из режима пониженного энергопотребления через 1 мс. Вызов функции HAL_PWR_EnterSLEEPMode() в строке 96 переводит микроконтроллер в спящий режим, пока он не пробудится при нажатии пользовательской кнопки USER (микроконтроллер пробуждается, потому что мы конфигурируем соответствующий IRQ, который вызывает условие выхода инструкции WFI из режима пониженного энергопотребления). Функция StopMode(), не показанная здесь, практически идентична функции SleepMode(), за исключением того факта, что она вызывает функцию HAL_PWR_EnterSTOPMode() для перевода микроконтроллера в режим останова.

Имя файла: src/main-ex1.c

140void StandbyMode(void) {

141MX_GPIO_Deinit();

142

143/* Эта процедура взята из перечня аппаратных ошибок (Errata sheet) STM32F030 */

144__HAL_RCC_PWR_CLK_ENABLE();

145

146 HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1); 147

148/* Сброс Флага пробуждения периферийного устройства PWR */

149__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

150

151/* Включение вывода WKUP */

152HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);

154/* Переход в режим ожидания (STANDBY) */

155HAL_PWR_EnterSTANDBYMode();

156}

Наконец, строки [140:156] определяют функцию StandbyMode(). Здесь мы следуем процедуре, описанной в перечне аппаратных ошибок STM32F30, поскольку на этот микроконтроллер оказывает влияние аппаратная ошибка, которая не позволяет ЦПУ перейти в режим ожидания: сначала мы должны отключить вывод PWR_WAKEUP_PIN1, а затем сбросить флаг пробуждения в регистре PWR->CSR и повторно включить вывод пробуждения, который в микроконтроллере STM32F030 соответствует выводу PA0.

Микроконтроллеры STM32 обычно имеют два вывода пробуждения, которые называются PWR_WAKEUP_PIN1 и PWR_WAKEUP_PIN2. Для многих микроконтроллеров STM32 с корпусом LQFP64 второй вывод пробуждения соответствует PC13, который подключен к пользовательской кнопке USER на всех платах Nucleo (кроме Nucleo-F302, где он подключен к выводу PB13). Тем не менее, мы не можем использовать PWR_WAKEUP_PIN2 в нашем примере, потому что этот вывод подтянут к питанию резистором на печатной плате. Когда мы конфигурируем выводы пробуждения в сочетании с режимом ожидания, мы не используем

Управление питанием

497

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

STM32F030.

Рисунок 5: Как измерить энергопотребление микроконтроллера на плате Nucleo

Платы Nucleo позволяют измерять потребление тока микроконтроллером при помощи штыревого разъема IDD. Перед началом измерений необходимо установить соединение с платой, как показано на рисунке 5, сняв перемычку IDD и подключив щупы амперметра. Убедитесь, что амперметр настроен на шкалу мА. Таким образом, вы можете увидеть потребление энергии в каждом режиме питания.

19.3.3.Важное предупреждение о микроконтроллерах

STM32F1

Во время разработки примеров бестикового режима работы во FreeRTOS в соответствующей главе я столкнулся с неприятным поведением микроконтроллера STM32F103 при переходе в режим останова с помощью процедуры HAL_PWR_EnterSTOPMode() от HAL CubeF1. В частности, возникшая проблема связана с выходом из этого режима пониженного энергопотребления, когда микроконтроллер переходит в него при помощи инструкции WFI. В этом специфичном сценарии микроконтроллер правильно переходит в режим останова, но, когда он просыпается от прерывания, он немедленно генерирует исключение тяжелого отказа Hard Fault. Я пришел к выводу, что разработчики ST не следуют советам ARM при переходе в режимы пониженного энергопотребления в процессорах Cortex-M3, как сообщается здесь16.

16 http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHICBGB.html

Управление питанием

498

Изменение процедуры HAL следующим образом решило проблему:

 

1 void HAL_PWR_EnterSTOPMode(uint32_t Regulator, uint8_t STOPEntry) {

2/* Проверка параметров */

3assert_param(IS_PWR_REGULATOR(Regulator));

4assert_param(IS_PWR_STOP_ENTRY(STOPEntry));

6/* Сброс бита PDDS в регистре PWR, чтобы указать о наступающем переходе в режим останова \

7(STOP) при переходе ЦПУ в режим глубокого сна (Deepsleep) */

8CLEAR_BIT(PWR->CR, PWR_CR_PDDS);

10/* Выбор режима регулятора напряжения установкой бита LPDS в регистре PWR в соответствии \

11со значением параметра Regulator */

12MODIFY_REG(PWR->CR, PWR_CR_LPDS, Regulator);

13

14/* Установка бита SLEEPDEEP регистра управления системой Cortex */

15SET_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));

16

17/* Выбор перехода в режим останова -----------------------------------------*/

18if(STOPEntry == PWR_STOPENTRY_WFI)

19{

20/* Запрос Wait For Interrupt */

21__DSB(); // Добавлено мной

22__WFI();

23__ISB(); // Добавлено мной

24}

25else

26{

27/* Запрос Wait For Event */

28__SEV();

29PWR_OverloadWfe(); /* локальное переопределение WFE */

30PWR_OverloadWfe(); /* локальное переопределение WFE */

31}

32/* Сброс бита SLEEPDEEP регистра управления системой Cortex */

33CLEAR_BIT(SCB->SCR, ((uint32_t)SCB_SCR_SLEEPDEEP_Msk));

34}

Изменение состоит просто в добавлении двух инструкций барьера памяти до и после инструкции WFI, как показано в строках 21 и 23.

Я задал вопрос по этой проблеме на официальном форуме ST17, но до сих пор так и не получил ответа на момент написания данной главы, и подозреваю, что ничего не получу.

17 https://community.st.com/s/question/0D50X00009XkfT2SAJ/question-regarding-implementation-of- halpwrenterstopmode-and-other-lowpower-routines-in-f1-hal