Слой аппаратной абстракции HAL

Последняя библиотека — слой аппаратной абстракции (англ. Hardware Abstraction Layer, HAL). Ее основные задачи — сократить время разработки и позволить писать портируемый на любое семейство STM32 (F0, F1 и т.д.) код. С одной стороны, она похожа на стандартную библиотеку: для инициализации периферийного блока используется структура. Перепишем инициализацию порта ввода-вывода с использованием библиотеки HAL:

Код не сильно отличается от StdPeriph или LL, а именование файлов осуществляется схожим образом: stm32f1xx_hal_ppp.c / stm32f1xx_hal_ppp.h, где ppp — название периферии. Учитывая опыт создания стандартной библиотеки, в HAL введено разделение на общие (т.е. применимые для всех семейств МК) и специфические функции. Вторые, если они есть, помещаются в отдельный файл stm32f1xx_hal_ppp_ex.c / stm32f1xx_hal_ppp_ex.h (суффикс ex происходит от слова extend, расширенный).

Все модули подключаются через конфигурационный файл stm32f1xx_hal_conf.h:

Функции для работы с системными ресурсами (SysTick и NVIC) переопределены в файлах stm32f1xx_hal_cortex.c / stm32f1xx_hal_cortex.h. Инициализация периферийных устройств осуществляется через файл stm32f1xx_hal_msp.c.

hal

Первое, что должно быть сделано в функции main() — вызов HAL_Init(), которая настраивает доступ к флеш-памяти и системный таймер. По умолчанию он используется для реализации функции задержки, по этой причине при использовании операционной системы реального времени в качестве источника системного тика должен быть выбран другой таймер.

Не надо думать, что на этом все особенности библиотеки заканчиваются. Если речь не идет об общих или системных ресурсах (GPIO, SysTick, NVIC, PWR, RCC, FLASH), вводится еще одна сущность — дескриптор (англ. handle). Он используется для полного описания объекта1 в системе. Если мы говорим о модуле коммуникации (USART, SPI, I2C и т.д.), мы должны помнить о его основной задаче — обмене данными, которые обычно хранятся в буфере. Проблема в том, что нужно завести как минимум два массива (на прием и на отправку) плюс еще две переменных (или макроса) с размером буферов. У блока может быть несколько режимов работы (через USART реализуется IrDA и SMARTCARD, например), кроме того, сам блок имеет внутреннее состояние (он сейчас что-то отправляет или, наоборот, ожидает команд). В устройстве при этом может находится несколько таких блоков — два, три, кто знает? В итоге получается, что для обслуживания одного «экземпляра» (англ. instance) USART требуется создать кучу переменных, в именовании которых можно легко запутаться. Логичным решением является обертка всех этих переменных в структуру.

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

Взгляните на фрагмент из модуля SPI:

Блокировка предотвращает возможность одновременного доступа к ресурсу (в данном случае к периферийному блоку SPI). Сама функция, как можно заметить, возвращает код ошибки: если линия занята, то вернется HAL_BUSY, а если операция завершена успешно — OK.

Кроме всего прочего, библиотека предусматривает максимальное время работы (англ. time-out) с периферийным блоком. Если блок используется дольше определенного времени, функция завершает свою работу и возвращает код ошибки HAL_TIMEOUT.

Также HAL реализует механизм пользовательских обратных функций (англ. user-callback).

Все они в файле библиотеки объявляются с «модификатором» __weak2 (слабый) и могут быть переопределены разработчиком (менять их внутри библиотеки не нужно!)

Если необходимо произвести действие по событию завершения отправки сообщения, то функцию HAL_UART_TxCpltCallback() нужно поместить в файл обработчиков прерываний stm32fxx_it.c. Библиотека достаточно сильно абстрагирует от железа. Обязательно загляните в файлы исходного кода и ужаснитесь, какой ценой. Детальное описание библиотеки можно найти в документе UM1850.

Отношение к библиотеке HAL у многих разработчиков отрицательное: она очень громоздкая и запутанная. Еще и использует goto, что многими считается плохой практикой. Вот комментарий одного из пользователей на stackoverflow:

My advice: forget the hal. Use bare registers instead

Мой совет: забудь про HAL. Работай с голыми регистрами.

Если, однако, вы работаете с каким-нибудь stm32f7, у вас высокая тактовая частота и мегабайты flash-памяти, HAL можно использовать без раздумий — нужно только проникнуться ее «философией». Подключить библиотеку к проекту можно, определив макрос USE_HAL_DRIVER в настройках среды разработки.


Назад | Оглавление | Дальше


1 Си не является объектно-ориентированным языком программирования.
2 Атрибут __attribute__((weak)) позволяет сообщить компоновщику, что данная функция «слабая», т.е. имеет «меньший приоритет». Если находится такая же функция без данного атрибута, то она считается «сильной» и перезаписывает «слабую».