| name | embedded-cpp-guidelines |
| description | C++17 embedded systems development guidelines. Pure virtual HAL interface abstraction, RAII resource management, lightweight handwritten Mock strategy without Google Mock macros, Google Test with CMake FetchContent, dependency injection, enum class type safety, noexcept patterns. Use when writing firmware, embedded drivers, HAL abstraction layers, MCU code, platform portability, unit testing embedded C++ code, or working with IGpio/IUart/ISpi/II2c interfaces. |
| triggers | ["embedded C++","C++ firmware","HAL interface","embedded HAL","pure virtual embedded","RAII embedded","mock embedded","IGpio","IUart","embedded unit test","Google Test embedded","CMake embedded","STM32 C++","MCU C++"] |
Embedded C++ Guidelines
๊ฐ์
C++17 ์๋ฒ ๋๋ ์์คํ
์์ ํ๋ซํผ ์ด์์ฑ + ๋จ์ ํ
์คํธ ๊ฐ๋ฅ์ฑ์ ๋์์ ํ๋ณดํ๊ธฐ ์ํ ํต์ฌ ํจํด.
ํ๋์จ์ด ์์ด ํ
์คํธ ๊ฐ๋ฅํ ์ฝ๋ ๊ตฌ์กฐ๋ฅผ ๋ชฉํ๋ก ํ๋ค.
์ฐธ์กฐ ๋ฒ ์ด์ค ์ฝ๋: base/cpp-embedded/
ํจํด 1 โ ์์ ๊ฐ์ HAL ์ธํฐํ์ด์ค
์์น: ๋ชจ๋ ํ๋์จ์ด ์ ๊ทผ์ ์์ ๊ฐ์ ์ธํฐํ์ด์ค(Abstract Class)๋ฅผ ํตํ๋ค.
๋น์ฆ๋์ค ๋ก์ง์ ๊ตฌ์ฒด ๊ตฌํ์ด ์๋ ์ธํฐํ์ด์ค์๋ง ์์กดํ๋ค (DIP).
#pragma once
#include <cstdint>
namespace hal {
class IGpio {
public:
enum class Direction : uint8_t { Input, Output };
enum class Level : uint8_t { Low = 0, High = 1 };
enum class PullMode : uint8_t { None, PullUp, PullDown };
virtual ~IGpio() = default;
virtual void setDirection(Direction dir) noexcept = 0;
virtual void write(Level level) noexcept = 0;
[[nodiscard]]
virtual Level read() const noexcept = 0;
virtual void toggle() noexcept = 0;
virtual void setPull(PullMode mode) noexcept = 0;
protected:
IGpio() = default;
IGpio(const IGpio&) = delete;
IGpio& operator=(const IGpio&) = delete;
IGpio(IGpio&&) = delete;
IGpio& operator=(IGpio&&) = delete;
};
}
ํต์ฌ ๊ท์น:
- ์๋ฉธ์๋ ๋ฐ๋์
virtual ~IGpio() = default;
- ๋ชจ๋ enum์
enum class๋ก ํ์
์์ ์ฑ ํ๋ณด
- ์ฝ๊ธฐ ์ ์ฉ ๋ฉ์๋๋
[[nodiscard]] + const noexcept
- ์ธํฐํ์ด์ค๋ ๋ณต์ฌ/๋์
๊ธ์ง (์ฌ๋ผ์ด์ฑ ๋ฐฉ์ง)
- ๋ค์์คํ์ด์ค:
hal:: (์ธํฐํ์ด์ค), platform:: (๊ตฌํ), mock:: (ํ
์คํธ์ฉ)
ํ๋ซํผ ๊ตฌํ โ ์คํ
๊ตฌ์กฐ (์ค์ MCU ์ด์ ์ ๋ณธ๋ฌธ๋ง ๊ต์ฒด):
#pragma once
#include "../../include/hal/IGpio.hpp"
namespace platform::stm32 {
class Gpio final : public hal::IGpio {
public:
explicit Gpio(uint8_t port, uint8_t pin) noexcept;
~Gpio() override = default;
void setDirection(Direction dir) noexcept override;
void write(Level level) noexcept override;
Level read() const noexcept override;
void toggle() noexcept override;
void setPull(PullMode mode) noexcept override;
private:
uint8_t m_port;
uint8_t m_pin;
Level m_level{Level::Low};
Direction m_direction{Direction::Output};
};
}
ํจํด 2 โ RAII ๋ฆฌ์์ค ๊ด๋ฆฌ
์์น: ํ๋์จ์ด ๋ฆฌ์์ค(ํ ์ํ, ๋ฒํผ, ๋ฝ)๋ ์์ฑ์์์ ์ด๊ธฐํํ๊ณ ์๋ฉธ์์์ ํด์ ํ๋ค.
์์ธ ์๋ ์๋ฒ ๋๋ ํ๊ฒฝ์์ ๋ฆฌ์์ค ๋์๋ฅผ ๋ฐฉ์งํ๋ ์ ์ผํ ์ ๋ขฐํ ์ ์๋ ๋ฐฉ๋ฒ.
#pragma once
#include "../../include/hal/IGpio.hpp"
namespace drivers {
class Led {
public:
enum class ActiveLevel : uint8_t { High, Low };
explicit Led(hal::IGpio& gpio,
ActiveLevel activeLevel = ActiveLevel::High) noexcept;
~Led() noexcept;
Led(const Led&) = delete;
Led& operator=(const Led&) = delete;
Led(Led&&) = delete;
Led& operator=(Led&&) = delete;
void on() noexcept;
void off() noexcept;
void toggle() noexcept;
[[nodiscard]] bool isOn() const noexcept;
private:
hal::IGpio& m_gpio;
ActiveLevel m_activeLevel;
bool m_isOn{false};
hal::IGpio::Level activeGpioLevel() const noexcept;
hal::IGpio::Level inactiveGpioLevel() const noexcept;
};
}
#include "../include/Led.hpp"
namespace drivers {
Led::Led(hal::IGpio& gpio, ActiveLevel activeLevel) noexcept
: m_gpio{gpio}, m_activeLevel{activeLevel} {
m_gpio.setDirection(hal::IGpio::Direction::Output);
off();
}
Led::~Led() noexcept {
off();
}
void Led::on() noexcept { m_gpio.write(activeGpioLevel()); m_isOn = true; }
void Led::off() noexcept { m_gpio.write(inactiveGpioLevel()); m_isOn = false; }
void Led::toggle() noexcept { m_isOn ? off() : on(); }
bool Led::isOn() const noexcept { return m_isOn; }
hal::IGpio::Level Led::activeGpioLevel() const noexcept {
return (m_activeLevel == ActiveLevel::High)
? hal::IGpio::Level::High : hal::IGpio::Level::Low;
}
hal::IGpio::Level Led::inactiveGpioLevel() const noexcept {
return (m_activeLevel == ActiveLevel::High)
? hal::IGpio::Level::Low : hal::IGpio::Level::High;
}
}
RAII ํต์ฌ ๊ท์น:
- ๋๋ผ์ด๋ฒ ์์ฑ์:
init() ํธ์ถ ์์ด ์์ฑ์์์ ์ฆ์ ์ด๊ธฐํ
- ๋๋ผ์ด๋ฒ ์๋ฉธ์: GPIO๋ฅผ ์์ ํ ์ํ(Low ๋๋ Off)๋ก ๋ณต์
- ๋๋ผ์ด๋ฒ๋ HAL ์ธํฐํ์ด์ค๋ฅผ ์ฐธ์กฐ๋ก ๋ฐ์ โ ์์ ํ์ง ์์ (์๋ช
๊ด๋ฆฌ ๋ถ๋ฆฌ)
- ๋์ ํ ๋น ๊ธ์ง:
new / delete ์ฌ์ฉ ๊ธ์ง, ์ ์ ๋๋ ์คํ ํ ๋น๋ง ์ฌ์ฉ
ํจํด 3 โ ๊ฒฝ๋ ์ง์ ์์ฑ Mock ์ ๋ต
์์น: Google Mock(MOCK_METHOD, EXPECT_CALL) ๋์ ์ธํฐํ์ด์ค๋ฅผ ์ง์ ์์ํ ๊ฒฝ๋ Mock ํด๋์ค๋ฅผ ์์ฑํ๋ค.
ํธ์ถ ํ์์ ๋ง์ง๋ง ๊ฐ์ public ๋ฉค๋ฒ๋ก ์ถ์ ํ๋ฉด ๋๋ถ๋ถ์ ์๋ฒ ๋๋ ๋จ์ ํ
์คํธ์ ์ถฉ๋ถํ๋ค.
#pragma once
#include "../include/hal/IGpio.hpp"
namespace mock {
class MockGpio final : public hal::IGpio {
public:
uint32_t writeCount{0};
uint32_t toggleCount{0};
Level lastLevel{Level::Low};
Direction lastDirection{Direction::Output};
PullMode lastPullMode{PullMode::None};
void setReadResponse(Level level) noexcept { m_readResponse = level; }
void setDirection(Direction dir) noexcept override { lastDirection = dir; }
void write(Level level) noexcept override { ++writeCount; lastLevel = level; }
Level read() const noexcept override { return m_readResponse; }
void toggle() noexcept override {
++toggleCount;
lastLevel = (lastLevel == Level::High) ? Level::Low : Level::High;
}
void setPull(PullMode mode) noexcept override { lastPullMode = mode; }
void reset() noexcept {
writeCount = toggleCount = 0;
lastLevel = Level::Low;
lastDirection = Direction::Output;
lastPullMode = PullMode::None;
m_readResponse = Level::Low;
}
private:
Level m_readResponse{Level::Low};
};
}
Mock ํ
์คํธ ํจํด:
#include <gtest/gtest.h>
#include "../mock/MockGpio.hpp"
#include "../drivers/include/Led.hpp"
TEST(LedDriverTest, Constructor_SetsOutput_AndOff) {
mock::MockGpio gpio;
drivers::Led led{gpio};
EXPECT_EQ(gpio.lastDirection, hal::IGpio::Direction::Output);
EXPECT_EQ(gpio.lastLevel, hal::IGpio::Level::Low);
EXPECT_FALSE(led.isOn());
}
TEST(LedDriverTest, Destructor_TurnsOffLed) {
mock::MockGpio gpio;
{
drivers::Led led{gpio, drivers::Led::ActiveLevel::High};
led.on();
EXPECT_EQ(gpio.lastLevel, hal::IGpio::Level::High);
}
EXPECT_EQ(gpio.lastLevel, hal::IGpio::Level::Low);
}
TEST(LedDriverTest, Toggle_ActiveLow) {
mock::MockGpio gpio;
drivers::Led led{gpio, drivers::Led::ActiveLevel::Low};
led.on();
EXPECT_EQ(gpio.lastLevel, hal::IGpio::Level::Low);
led.off();
EXPECT_EQ(gpio.lastLevel, hal::IGpio::Level::High);
}
ํจํด 4 โ CMake + Google Test FetchContent
์์น: Google Test๋ฅผ FetchContent๋ก ์๋ ๋ค์ด๋ก๋ํ์ฌ ๋ณ๋ ์ค์น ์์ด ๋น๋ํ๋ค.
INTERFACE ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ํค๋ ์ ์ฉ ์ธํฐํ์ด์ค๋ฅผ ๋
ธ์ถํ๊ณ , STATIC ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋๋ผ์ด๋ฒ๋ฅผ ๋ถ๋ฆฌํ๋ค.
# CMakeLists.txt (๋ฃจํธ)
cmake_minimum_required(VERSION 3.21)
project(CppEmbeddedBase LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# ๊ฒฝ๊ณ ์ค์
if(MSVC)
add_compile_options(/W4 /WX)
else()
add_compile_options(-Wall -Wextra -Wpedantic -Werror)
endif()
# โโ Google Test ์๋ ๋ค์ด๋ก๋ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Windows ์ ์ ๋ฐํ์
FetchContent_MakeAvailable(googletest)
# โโ HAL ์ธํฐํ์ด์ค (ํค๋ ์ ์ฉ) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
add_library(hal_interfaces INTERFACE)
target_include_directories(hal_interfaces INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_compile_features(hal_interfaces INTERFACE cxx_std_17)
# โโ HAL ๋๋ผ์ด๋ฒ (์ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
add_library(hal_drivers STATIC
drivers/src/Led.cpp
drivers/src/Console.cpp
platform/stm32/Gpio.cpp # ํ๋ซํผ ์คํ
โ ์ค์ MCU ์ด์ ์ ๊ต์ฒด
platform/stm32/Uart.cpp
)
target_include_directories(hal_drivers PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/drivers/include
${CMAKE_CURRENT_SOURCE_DIR}/platform/stm32 # ์คํ
ํค๋ ํฌํจ ๊ฒฝ๋ก
)
target_link_libraries(hal_drivers PUBLIC hal_interfaces)
option(BUILD_TESTING "Build unit tests" ON)
if(BUILD_TESTING)
add_subdirectory(tests)
endif()
# tests/CMakeLists.txt
enable_testing() # CTest ํตํฉ ํ์ โ ๋๋ฝ ์ ctest ๋ช
๋ น์ด ํ
์คํธ๋ฅผ ์ฐพ์ง ๋ชปํจ
add_executable(run_tests
test_gpio.cpp
test_uart.cpp
test_led_driver.cpp
)
target_include_directories(run_tests PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/mock
${CMAKE_SOURCE_DIR}/drivers/include
)
target_link_libraries(run_tests PRIVATE hal_drivers GTest::gtest_main)
include(GoogleTest)
gtest_discover_tests(run_tests)
๋น๋ ์คํ:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
cd build && ctest --output-on-failure
๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ
base/cpp-embedded/
โโโ include/hal/ โ ๊ณต๊ฐ ์ธํฐํ์ด์ค (IGpio, IUart, ISpi, II2c)
โโโ platform/stm32/ โ ํ๋ซํผ ์คํ
๊ตฌํ (Gpio.cpp, Uart.cpp)
โโโ mock/ โ ํ
์คํธ์ฉ Mock (MockGpio.hpp, MockUart.hpp)
โโโ drivers/ โ ์ฌ์ฌ์ฉ ๋๋ผ์ด๋ฒ (Led, Console)
โ โโโ include/
โ โโโ src/
โโโ tests/ โ Google Test ๋จ์ ํ
์คํธ
โโโ examples/blinky/ โ Blinky ์ฌ์ฉ ์์
โโโ CMakeLists.txt
์ํฐํจํด
| ์ํฐํจํด | ์ฌ๋ฐ๋ฅธ ํจํด |
|---|
new / delete ์ฌ์ฉ | ์ ์ /์คํ ํ ๋น, RAII |
HAL_GPIO_WritePin() ์ง์ ํธ์ถ | IGpio::write() ์ธํฐํ์ด์ค ์ฌ์ฉ |
ํจ์ ๋ด๋ถ์์ Gpio gpio; ์์ฑ | ์์ฑ์ ๋งค๊ฐ๋ณ์๋ก hal::IGpio& ์ฃผ์
|
int ํ์
GPIO ๋ ๋ฒจ (0, 1) | enum class Level { Low, High } |
Google Mock MOCK_METHOD ๋งคํฌ๋ก | ์ง์ ์์ฑ Mock ํด๋์ค (๊ต์ก์ , ๊ฒฝ๋) |
virtual ์๋ฉธ์ ๋๋ฝ | virtual ~IBase() = default; ํ์ |
| ์์ธ(exception) ์ฌ์ฉ | noexcept + ๋ฐํ๊ฐ์ผ๋ก ์ค๋ฅ ์ ํ |
std::cout ๋๋ฒ๊ทธ ์ถ๋ ฅ | Console ๋๋ผ์ด๋ฒ (IUart ๊ธฐ๋ฐ) ์ฌ์ฉ |
| ํ๋ซํผ ํค๋ ๋น์ฆ๋์ค ๋ก์ง์ ํฌํจ | HAL ์ธํฐํ์ด์ค๋ง ํฌํจ, ํ๋ซํผ ํค๋ ๋ถ๋ฆฌ |
enum (ํ์
๋ฏธ์ฝ) | enum class : uint8_t |
C++17 ์๋ฒ ๋๋ ํต์ฌ ํค์๋
[[nodiscard]]
[[maybe_unused]]
noexcept
override
final
constexpr
if constexpr(...)
std::array<T, N>
std::optional<T>
std::string_view
MCU ํ๊ฒฝ๋ณ STL ์ฌ์ฉ ์ง์นจ:
- Cortex-M3/M4/M7 + newlib ํ๋ฒ์ :
std::array, std::optional, std::string_view ์ฌ์ฉ ๊ฐ๋ฅ
- Cortex-M0/M0+ + newlib-nano:
std::array๋ง ์์ . ๋๋จธ์ง๋ ์ฝ๋ ํฌ๊ธฐ/ํ ํ์ธ ํ์
- AUTOSAR/MISRA ์ค์ ํ๋ก์ ํธ: STL ์์ ๊ธ์ง, ์ง์ ๊ตฌํ ๋ฐฐ์ด/์ต์
๋ ์ฌ์ฉ
์ฐธ์กฐ ์ฝ๋: base/cpp-embedded/
ํ
์คํธ ๊ฒฐ๊ณผ: Google Test 43/43 PASSED (LedDriver 12 + MockUart 16 + MockGpio 15)
๋น๋ ํ๊ฒฝ: CMake 3.21+, C++17, GoogleTest v1.14.0 (FetchContent)