글의 순서
MCU용 HAL이란?
HAL의 정의 및 필요성
**HAL(Hardware Abstraction Layer)**은 마이크로컨트롤러(MCU) 기반 시스템 개발에서 하드웨어에 대한 직접적인 접근 없이, 추상화된 API를 활용해 제어와 프로그래밍을 가능하게 해주는 계층입니다.
개발자는 복잡한 레지스터 설정 대신, HAL에서 제공하는 함수만 사용해 빠르고 안정적으로 주변 장치를 제어할 수 있습니다.
벡터코리아와 인피니언이 최근 발표한 HAL 역시 이 접근 방식을 따릅니다.
이들은 메모리 풋프린트가 제한된 MCU를 위해 경량 HAL을 설계했습니다.
개발자는 실제 하드웨어 세부 정보를 알지 못해도, 표준화된 인터페이스를 통해 동일한 개발 환경을 유지할 수 있게 됩니다.
HAL의 핵심 필요성은 다음과 같습니다:
-
하드웨어 독립적인 코드 작성
-
펌웨어 유지보수 용이성 증가
-
여러 MCU 시리즈 간 코드 이식성 확보
이러한 특성으로 인해 초보자부터 전문가까지 HAL을 활용한 개발 방식이 널리 채택되고 있습니다.
HAL의 주요 기능
하드웨어 추상화 계층의 역할
HAL은 내부적으로 CMSIS(Core Microcontroller Software Interface Standard) 위에서 동작합니다.
이는 ARM Cortex-M 계열 MCU와의 호환을 위한 인터페이스입니다.
HAL은 이와 같은 표준 위에 개별 MCU의 하드웨어 특성을 추상화하여 사용자에게 최종 API를 제공합니다.
예를 들어:
- HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
이처럼 간단한 함수로도 GPIO 제어가 가능해지며, 하드웨어 레벨의 복잡한 설정을 생략할 수 있습니다.
코드 이식성과 유지보수성 향상
STM32F0, F1, F4, H7 등 다양한 STM32 MCU 간에 HAL을 사용하면 동일한 주변장치 제어 함수 호출만으로 다른 칩으로의 포팅이 쉬워집니다.
또한 코드 가독성이 좋아지고, 개발 기간이 단축되며, 유지보수도 간편해집니다.
더불어 벡터코리아의 HAL은 PSoC™4 HV 드라이버에 적용되어, ECU 입출력을 위한 ‘마이크로사 IO’ 인터페이스와 통합됨으로써 자동차 산업 등 고신뢰 요구 환경에도 활용 가능합니다.
STM32에서 HAL 사용 시 고려사항
HAL과 LL 드라이버 비교
STM32의 HAL과 LL(Low Layer)은 같은 STM32Cube 드라이버 프레임워크 안에 존재하지만 성격이 다릅니다.
| 항목 | HAL | LL |
|---|---|---|
| 추상화 수준 | 높음 | 낮음 |
| 코드 복잡도 | 낮음 | 높음 |
| 개발 속도 | 빠름 | 느림 |
| 성능/메모리 사용 | 낮음 | 높음 |
| 실시간 응용 적합성 | 낮음 | 높음 |
성능이 중요한 실시간 애플리케이션에서는 LL이 유리할 수 있으며, 일반적인 개발에서는 HAL이 더 적합합니다.
산업 현장에서는 HAL을 기본으로 작성하고, 성능이 중요한 부분에 LL을 혼합하여 사용하는 하이브리드 접근법도 널리 활용됩니다.
성능 최적화 팁
-
불필요한 HAL 함수 호출을 줄이고 직접 레지스터 접근을 피해라.
-
DMA 및 인터럽트를 적극적으로 활용하면 성능이 향상될 수 있다.
-
STM32CubeMX에서 HAL 코드 생성을 할 때 꼭 필요한 주변장치만 활성화해 코드 사이즈를 최소화하라.
STM32에서 HAL을 사용하는 방법
STM32CubeMX를 통한 코드 생성
STM32CubeMX는 STMicroelectronics에서 제공하는 GUI 기반 설정 도구입니다.
HAL 기반 프로젝트 생성 방법은 다음과 같습니다:
-
MCU 또는 보드 선택 후 프로젝트 생성
-
GPIO, UART, TIM 등 주변장치 설정
-
클럭 구성 및 핀맵 세팅
-
'HAL 드라이버 사용' 옵션 체크
-
코드 생성 클릭 (STM32CubeIDE로 연동 가능)
생성된 프로젝트는 바로 HAL API를 사용하는 코드 형태로 준비됩니다.
HAL 드라이버 구조 이해
HAL 라이브러리는 각 주변 장치에 대응하는 드라이버 세트를 포함합니다.
주요 예:
-
GPIO: HAL_GPIO_TogglePin(), HAL_GPIO_ReadPin()
-
UART: HAL_UART_Transmit(), HAL_UART_Receive()
-
SPI/I2C: HAL_SPI_Transmit(), HAL_I2C_Master_Transmit()
-
TIM: HAL_TIM_PWM_Start(), HAL_TIM_Base_Start()
실행 예제를 코딩할 때도 HAL API 이름이 직관적이어서, 문서 없이도 상당 부분 유추가 가능합니다.
이 역시 초급 개발자가 HAL을 선호하는 이유입니다.



