C로 HAL(하드웨어 추상화 계층) 작성
홈페이지홈페이지 > 블로그 > C로 HAL(하드웨어 추상화 계층) 작성

C로 HAL(하드웨어 추상화 계층) 작성

Nov 22, 2023

베냉의 야곱 | 2023년 5월 19일

HAL(하드웨어 추상화 계층)은 모든 임베디드 소프트웨어 애플리케이션에 중요한 계층입니다. HAL을 사용하면 개발자가 애플리케이션 코드에서 하드웨어 세부 정보를 추상화하거나 분리할 수 있습니다. 하드웨어를 분리하면 하드웨어에 대한 애플리케이션의 종속성이 제거됩니다. 즉, 대상 외부, 즉 호스트에서 작성하고 테스트할 수 있는 완벽한 위치에 있다는 의미입니다. 그러면 개발자는 애플리케이션을 훨씬 빠르게 시뮬레이션, 에뮬레이션 및 테스트하여 버그를 제거하고 시장 출시 기간을 단축하며 전체 개발 비용을 절감할 수 있습니다. 임베디드 개발자가 C로 작성된 HAL을 설계하고 사용하는 방법을 살펴보겠습니다.

하드웨어에 직접 액세스하는 임베디드 애플리케이션 모듈을 찾는 것은 비교적 일반적입니다. 이렇게 하면 애플리케이션 작성이 더 간단해지지만 애플리케이션이 하드웨어에 긴밀하게 결합되기 때문에 좋지 않은 프로그래밍 방식이기도 합니다. 이것이 큰 문제가 아니라고 생각할 수도 있습니다. 결국 둘 이상의 하드웨어 세트에서 애플리케이션을 실행하거나 코드를 이식해야 하는 사람이 누구이겠습니까? 그렇다면 최근에 칩 부족으로 어려움을 겪고 다시 하드웨어를 재설계하는 것뿐만 아니라 모든 소프트웨어를 다시 작성해야 했던 모든 사람에게 안내해 드리겠습니다. 객체 지향 프로그래밍(OOP)의 많은 사람들이 이 문제를 해결하는 데 도움이 될 수 있는 종속성 반전 원칙으로 알고 있는 원칙이 있습니다.

종속성 역전 원칙은 "상위 수준 모듈은 하위 수준 모듈에 의존해서는 안되지만 둘 다 추상화에 의존해야 한다"고 명시합니다. 종속성 반전 원칙은 인터페이스나 추상 클래스를 사용하여 프로그래밍 언어에서 구현되는 경우가 많습니다. 예를 들어 읽기 및 쓰기 기능을 지원하는 디지털 입/출력(dio) 인터페이스를 C++로 작성한다면 다음과 같을 것입니다.

클래스 dio_base {

공공의:

가상 ~dio_base() = 기본값;

// 클래스 메소드

가상 무효 쓰기(dioPort_t 포트, dioPin_t 핀, dioState_t 상태) = 0;

가상 dioState_t read(dioPort_t 포트, dioPin_t 핀) = 0;

}

C++에 익숙한 분들은 가상 함수를 사용하여 인터페이스를 정의하고 있으며, 이를 위해서는 세부 사항을 구현하는 파생 클래스를 제공해야 한다는 것을 알 수 있습니다. 이러한 유형의 추상 클래스를 사용하면 애플리케이션에서 동적 다형성을 사용할 수 있습니다.

코드에서는 종속성이 어떻게 반전되었는지 확인하기가 어렵습니다. 대신 간단한 UML 다이어그램을 살펴보겠습니다. 아래 다이어그램에서 led_io 모듈은 종속성 주입을 통해 dio 인터페이스에 종속됩니다. led_io 객체가 생성되면 디지털 입력/출력 구현에 대한 포인터가 제공됩니다. 모든 마이크로컨트롤러 dio의 구현은 dio_base에 의해 정의된 dio 인터페이스도 충족해야 합니다.

위의 UML 클래스 다이어그램을 보면 이것이 C++와 같은 OOP 언어로 애플리케이션을 디자인하는 데는 좋지만 C에는 적용되지 않는다고 생각할 수도 있습니다. 그러나 실제로 C에서는 이러한 유형의 동작을 얻을 수 있습니다. 종속성을 반전시킵니다. C에서 구조를 사용하여 사용할 수 있는 간단한 트릭이 있습니다.

먼저 인터페이스를 디자인합니다. 인터페이스가 지원해야 한다고 생각하는 함수 서명을 작성하면 됩니다. 예를 들어, 인터페이스가 디지털 입력/출력의 초기화, 쓰기 및 읽기를 지원해야 한다고 결정한 경우 다음과 같이 함수를 나열할 수 있습니다.

void write(dioPort_t const 포트, dioPin_t const 핀, dioState_t const state);

dioState_t read(dioPort_t const 포트, dioPin_t const 핀);

이는 virtual 키워드와 순수 추상 클래스 정의(= 0)만 제외하고 이전에 C++ 추상 클래스에서 정의한 함수와 매우 비슷해 보입니다.

다음으로 이러한 함수를 typedef 구조체로 패키징할 수 있습니다. 구조체는 전체 dio 인터페이스를 포함하는 사용자 정의 유형처럼 작동합니다. 초기 코드는 다음과 같습니다.

read(port, pin) == dio->HIGH) ? dio->LOW : dio->HIGH);/p>

write(port, pin, state};/p>