이번에 STM32 개발 실습을 진행하면서 계속해서 HAL 이라는 키워드가 나왔다. 약간 namespace 같은건가? 싶었는데, 이게 라이브러리였다. 코드 작성하면서 눈치껏 이해했더니 이게 직접 레지스터를 건드리는 작업들이 복잡하고 실수가 많이 나올 수 있으니, 이 구조를 미리 묶어서 함수로 제공하는 형태인 것 같았다. 그래서 HAL 라이브러리가 무엇인지, 그래서 HAL을 써야하는지 말아야 하는지에 대해서 한 번 정리해보려고 한다.
HAL이 뭐냐면
<< HAL User Manual 공식 DOCS로부터 발췌 >>
The HAL drivers are designed to offer a rich set of APIs and to interact easily with the application upper layers. Each driver consists of a set of functions covering the most common peripheral features. The development of each driver is driven by a common API which standardizes the driver structure, the functions and the parameter names. The HAL drivers include a set of driver modules, each module being linked to a standalone peripheral. However, in some cases, the module is linked to a peripheral functional mode. As an example, several modules exist for the USART peripheral: UART driver module, USART driver module, SMARTCARD driver module and IRDA drive module.
HAL은 Hardware Abstraction Layer, 하드웨어 레지스터를 직접 제어하는 드라이버 코드로, 개발자가 작성하는 코드가 HW를 쉽게 제어할 수 있도록 풍부한 API를 제공해둔 것이다. HAL 드라이버는 여러 주변 장치에 대한 개별적인 모듈들로 구성되고 있으며, 각 모듈은 함수명, 파라미터명 등을 표준에 따라 작성해두어 개발 시 쉽게 활용할 수 있다.

레지스터를 조금 더 직접 제어하는 방식인 Low Layer 방식 (이하 LL)도 있다.
드라이버?
사실 나는 드라이버라는 단어도 잘 모르고 있었다. 막상 뭐냐고 말하라고 하니 못하겠더라.
드라이버는 HW와 SW를 연결하는 중간 계층의 코드이다. 드라이버의 함수 또는 API를 호출했을 때 레지스터에 대한 RW 작업을 대신 수행해준다면 이건 드라이버라고 볼 수 있다. HAL을 사용할 때 HAL_GPIO_WritePin 같은 코드들이 내 대신 Memory 상 GPIO의 특정 위치에 bit set 하는 작업을 해주는데, 이게 드라이버의 역할이라고 보면 된다.
HAL을 사용했을 때의 장점
우선 ‘Abstraction’ 이라는 이름에서부터 써야 할 것 같은 느낌이 팍팍 든다 ㅋㅋㅋ. 공식문서에 작성되어 있는 장점들을 한 번 살펴보자.
- 공통 Peripheral의 API가 동일하기 때문에, 코드를 한 번 작성하면 다른 STM32 시리즈의 보드에서도 동작할 수 있다.
- 낮은 성능의 CPU가 붙은 보드로 개발하다가 문제가 생겼을 때 더 좋은 성능의 보드로 옮겨서 그대로 이어 개발할 수 있음.
- 3가지 API의 프로그래밍 모델을 제공한다.
- Polling : 작업 완료할 때까지 CPU가 대기
- Interrupt : 특정한 이벤트가 발생하면 HW가 CPU에게 알림
- DMA : CPU의 개입 없이 HW가 메모리 전송을 수행
- RTOS 호환
- Reentrance API 지원 →
- 동일한 Peripheral이 여러개 있어도 괜찮음 (USART1, USART2, … 서로 간섭 X)
- 모든 HAL API에서 사용자 정의 함수를 호출할 수 있다.
- 공유자원에 대한 보호 매커니즘 지원 (세마포어 같은 거인듯 → 통신 중에 다른 Peripheral의 간섭 방지)
- 타임아웃 설정을 통해 무한루프에 빠지는 것 방지 가능
그래서 HAL을 써야하나?
에 대해서는 여전히 많은 갑론을박이 있는 것 같다.
- https://www.reddit.com/r/embedded/comments/g8lw5q/stm32_question_about_hal_libraries_vs_hardcoding/?tl=ko
- https://www.reddit.com/r/embedded/comments/1goxezz/stm32_hal_makes_you_weak/?tl=ko
- https://www.reddit.com/r/embedded/comments/18rb3m5/do_professionals_use_hal_in_their_work/?tl=ko
- https://www.reddit.com/r/embedded/comments/sg7vey/to_hal_or_not_to_hal_the_definitive_answer/?tl=ko&chainedPosts=t3_113geyh
HAL을 쓰면 안된다.
- 여전히 버그가 있다.
- HAL을 쓰면 성능이 별로다 (느리다)
- 내부 동작을 이미 이해하고 있으면 굳이 HAL을 써야하나?
- HAL 자체가 어떻게 동작하고 있는지를 추가로 공부해야한다. (버그가 아니라 기능이예요)
- 내부 구현이 좀 더럽고 불필요한게 많다. (그래서 HAL에 몇 줄 주석처리해도 동작함)
- HW의 동작방식을 알지 못한다면 형편없는 개발자이다.
HAL을 써야한다.
- 사람들은 내가 직접 작성한 코드보다 HAL 을 신뢰한다.
- 다음 사람이 유지보수 할 것도 생각해야 한다.
- 단순히 GPIO 정도 할거면 Register 건드리는걸로 개발해도 되는데, ETHERNET이나 File System으로 가면 복잡성이 너무 증가한다.
- 코드를 작성해서 돈을 받는게 아니라, 문제를 해결해서 돈을 받는거다. 비즈니스 로직에 집중할 수 있는 HAL을 사용하는게 좋다.
- 그렇다면 OS나 컴파일러는 왜 직접 만들어서 안쓰냐? 남들이 잘 만들어둔 것을 활용할 줄 아는 것이 부적절한게 아니다.
몇 가지 추가적인 인사이트
- SPI 기능의 경우에는 HAL 의 코드 부피가 커서 LL 방식을 사용하는게 더 효율적이다.
- 둘 다 할 줄 알아야 하는건 맞음 (HAL에서 문제 발생했을 때 원인을 찾고 해결할 줄 알아야 하니깐)
- HAL 같은 드라이버는 프로세서를 위한 제품이지, 프로젝트를 위한 제품이 아니다.
- LL 드라이버를 사용하는 곳도 종종 있다.
- 최적화가 필요할 때에는 레지스터로 구현이 필요하다.
- 의존성을 한 번 더 꺾어서 [ HAL → 프로젝트 자체 HAL → 프로젝트 코드 ] 으로 자체 HAL을 추가하는 곳도 있다.
- 필요한 부분에만 HAL 쓰면 되는거 아니냐?
공부하는 사람은 HAL을 써야하나?
라는 주제의 레딧 글도 있었다. https://www.reddit.com/r/embedded/comments/113geyh/make_an_argument_for_whether_a_beginner_should_or/?tl=ko
- 초보자는 둘 다 해야한다.
- HAL을 활용한 추상화와 코드 재사용에 대해서도 연습해야한다.
- 직접 레지스터를 제어하는 방식에 대해서도 연습해야한다.
- 그렇다면 레지스터를 사용하는 방법을 배우기 전에 추상화를 먼저 배워도 돼냐?
- 결과물을 만들어내면서 HW 까지 점차 내려가는게 더 좋다. (Top Down)
- 레지스터부터 다루면서 추상화 단계로 올라오는게 더 좋다. (Bottom Up)
그렇다면 나는 어떻게 할 것인가?
우선 HAL 없이 코드를 작성해보자.
- 데이터시트를 보고 레지스터를 조작하면서 코드를 작성할 줄 알아야 한다.
- 결국 나는 내부가 어떻게 동작하는지 알아야 한다.
- 90%는 HAL을 쓰고 10%만 직접 구현하게 된다는데, 이게 90%는 이미 어떻게 구현할 지 다 알고있고 10%는 최적화를 위해 구현 방법 + 개선 방법도 알고있어야 한다는 의미로 이해된다.
(혹시 다음에는 LL 드라이버를 활용하는 방법도 한 번 시도를 해봐야할까? 시간이 남는다면 요건 고려해봐야겠다.)
참고 문서
HAL 공식 유저 매뉴얼
ㅤ
'Embedded System > MCU' 카테고리의 다른 글
| [MCU] UART 구조와 이해 (0) | 2025.10.30 |
|---|---|
| [MCU] GPIO 실습 : USER Button과 LED 켜기 (0) | 2025.10.28 |
| [MCU] 빌드 프로세스와 컴파일 환경 (1) | 2025.10.27 |
| [MCU] GPIO의 하드웨어 구조와 데이터시트, 침침한 눈을 곁들인 (1) | 2025.10.24 |
| [STM32] STM32에서 DHT11 로 온습도 측정하기 (0) | 2025.09.29 |