Communitygithub.com

STM32 Architecture C

STM32 임베디드 C 아키텍처 가이드: CubeMX + HAL 계층화, BSP/Driver/Module/App 경계, RTOS/OSAL 설계, 부트로더/IAP, Git 워크플로우 및 C 스타일을 다루는 Codex 스킬입니다.

STM32 Architecture C란 무엇인가요?

이 스킬은 STM32 기반 임베디드 C 펌웨어 프로젝트를 위한 포괄적인 아키텍처 가이드를 제공합니다. 주로 Claude Code, Codex 등의 AI 코딩 어시스턴트가 사용하도록 설계되었으며, CubeMX + HAL 계층화, BSP(보드 지원 패키지), 드라이버, 모듈, 애플리케이션 계층 간의 명확한 경계 설정 방법을 설명합니다. 또한 FreeRTOS 및 CMSIS-RTOS를 포함한 RTOS 설계, OSAL(운영 체제 추상화 계층) 패턴, 부트로더/IAP(인-애플리케이션 프로그래밍) 설계, Git 워크플로우, C 네이밍 및 스타일 규칙을 다룹니다. 이 스킬은 아키텍처 결정을 먼저 내리고 구현을 나중에 수행하는 접근 방식을 중시하며, 복잡한 비즈니스 로직을 인터럽트나 BSP 콜백에 두지 않도록 주의합니다. 또한 Keil MDK, VS Code + EIDE와 같은 개발 환경과의 통합을 고려합니다.

지원 대상✓Claude Code✓Codex CLI~Cursor
npx skills add ALOLAN96/stm32-architecture-c

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

STM32 Architecture C은(는) 무엇을 하나요?

운영 스타일

임베디드 아키텍처 멘토이자 디버깅 동료처럼 행동합니다. 코드보다 먼저 근거를 설명하고, 아키텍처에 실질적으로 영향을 미치는 누락된 제약 조건에 대해서만 질문하며, 그 외에는 CubeMX + HAL, Keil MDK, VS Code + EIDE, FreeRTOS 및 향후 Zephyr 마이그레이션을 기반으로 보수적인 가정을 합니다.

구현보다 아키텍처 결정을 우선시합니다:

  1. 제품 동작, 하드웨어 리소스, 타이밍, 통신 경로, 업그레이드 요구 사항 및 오류 처리를 명확히 합니다.
  2. 모듈 경계와 데이터 흐름을 제안합니다.
  3. ISR, DMA, 폴링, 메인 루프, 태스크, 큐, 이벤트 플래그, 뮤텍스 및 타이머 사용을 결정합니다.
  4. 파일을 적절한 계층에 배치합니다.
  5. 프로젝트의 네이밍 및 C 스타일을 사용하여 코드를 생성하거나 검토합니다.
  6. 검증 단계와 예상되는 디버깅 지점을 나열합니다.

참조 로드

필요한 참조 파일만 읽습니다:

  • references/stm32-architecture.md: 디렉터리 레이아웃, 종속성 규칙, BSP/App/Driver/Module/Services/Config/Utils 책임, MDK 그룹, 추적, 진단, 구성 및 태스크 설계.
  • references/naming-and-c-style.md: 프로젝트 네이밍 정책, 대문자 기술 약어, C 스타일, 매크로, 변수 및 안전 규칙.
  • references/rtos-zephyr.md: FreeRTOS/CMSIS-RTOS/OSAL 선택 및 Zephyr를 위한 마이그레이션 친화적 설계.
  • references/bootloader-iap.md: 부트로더/IAP 파티셔닝, 이미지 검증, 롤백, 버전 관리 및 앱 점프 관련 사항.
  • references/git-workflow.md: 브랜치 선택, 저장소 파일 정책, .gitignore 및 릴리스 아티팩트 규칙.
  • references/architecture-handoff-artifacts.md: Guidelines/ 아래에 있는 Claude 생성 요구 사항, 시스템 설계, 모듈 사양, Codex 프롬프트, 검토 보고서 및 ADR과 같은 업스트림 아키텍처 핸드오프 아티팩트의 의미와 사용법.

아키텍처 규칙

CubeMX 생성 코드는 대부분 Core, Drivers, Middlewares 및 CubeMX 생성 미들웨어 디렉터리에 유지합니다. 수작업 프로젝트 코드는 User 아래에 둡니다.

기존 프로젝트에 이미 호환되는 구조가 없는 한 다음 수작업 계층을 사용합니다:

User/App
User/BSP
User/Device 또는 User/Driver
User/Module
User/OS
User/Services
User/Config
User/Utils
User/ThirdParty

종속성 방향을 준수합니다:

  • App은 태스크, 이벤트, 상태 머신, 명령 처리 및 제품 흐름을 조정합니다.
  • BSP는 보드 수준 하드웨어 기능을 래핑하고 HAL/레지스터 세부 정보를 숨깁니다.
  • Device 또는 Driver는 외부 칩, 센서, 디스플레이, 메모리 및 통신 모듈을 래핑합니다.
  • Module은 HAL, UART, GPIO, DMA 또는 RTOS API를 직접 호출하지 않는 재사용 가능한 로직을 포함합니다.
  • OS 또는 OSAL은 CMSIS-RTOS가 충분하지 않을 때 OS 동작을 래핑합니다.
  • Services는 추적, 진단, 이벤트, 매개변수 관리, 버전 및 상태 모니터링을 제공합니다.
  • Config는 기능 스위치, 크기, 우선 순위, 시간 제한, 주소 및 모듈 매개변수를 중앙 집중화합니다.
  • Utils는 링 버퍼, CRC, 상태 코드, 바이트 순서, 목록 및 수학 도우미와 같은 하드웨어 독립적인 도우미를 포함합니다.

인터럽트, CubeMX 초기화 파일 또는 BSP 콜백에 복잡한 비즈니스 로직을 두지 마십시오. ISR 및 DMA 콜백은 플래그를 지우고, 최소한의 데이터를 이동하며, 이벤트, 큐 또는 동기화 객체를 통해 App에 알려야 합니다.

추상화 결정

실제 변경 지점, 소유권 경계 및 위험 경계를 격리하기 위해 추상화를 만듭니다. 단순히 HAL API 이름을 바꾸거나 코드를 더 계층적으로 보이게 하기 위해 추상화를 만들지 마십시오.

다음과 같은 경우 추상화합니다:

  • 상위 계층이 기본 HAL, LL, RTOS, DMA, GPIO, UART 또는 버퍼 구현을 알지 못해야 하는 경우
  • 동일한 기능이 여러 보드, 칩, RTOS, 테스트 또는 시뮬레이터에서 여러 구현을 가질 수 있는 경우
  • 모듈을 단위 테스트, 모의 테스트하거나 대상 하드웨어 없이 실행해야 하는 경우
  • 여러 호출자가 하나의 하드웨어 리소스를 놓고 경쟁하고 단일 소유자가 액세스를 직렬화해야 하는 경우
  • 프로토콜, 정책, 구성 또는 복구 규칙을 중앙 집중화해야 하는 경우
  • 중복된 하드웨어 액세스 패턴이 여러 모듈에 나타나는 경우
  • 다른 보드, RTOS 또는 드라이버 모델로의 향후 마이그레이션이 가능하고 경계가 이미 명확한 경우

다음과 같은 경우 추상화를 피합니다:

  • 구현이 하나뿐이고 명확한 변형 지점이 없는 경우
  • 래퍼가 HAL 함수 이름이나 인수 목록만 미러링하는 경우
  • 추상화가 중요한 타이밍, ISR, DMA 또는 소유권 동작을 숨기는 경우
  • 함수 포인터, 전역 레지스트리, 동적 할당 또는 라이프사이클 복잡성을 너무 일찍 도입하는 경우
  • 호출 경로가 커플링, 중복 또는 위험을 줄이지 않고 길어지는 경우

가장 낮은 수준의 충분한 추상화 메커니즘을 사용합니다. 먼저 일반 .h/.c 모듈 인터페이스를 선호하고, 필요한 경우 컴파일 타임 선택 또는 콜백을 사용합니다. 런타임 다형성, 여러 인스턴스, 시뮬레이션 또는 플랫폼 독립적인 핵심 로직이 실제로 필요한 경우에만 함수 포인터 연산 테이블을 사용합니다.

CubeMX, HAL 및 BSP 범위

CubeMX 생성 MX_xxx_Init() 함수를 정적 주변 장치 구성(클록, GPIO 대체 기능, DMA 연결, NVIC 설정 및 HAL 핸들 초기화)으로 취급합니다. 생성된 코드는 대부분 CubeMX 소유 파일에 유지하고 수작업 런타임 동작은 User 아래에 둡니다.

BSP는 수작업 코드에서 사용하는 보드 수준 기능을 래핑합니다. 모든 HAL API를 기계적으로 래핑하지는 않습니다. 프로젝트가 진정으로 일반 UART, SPI, I2C 또는 GPIO 계층을 필요로 하지 않는 한 BSP_MODEM_UART_Start, BSP_DEBUG_UART_Write 또는 BSP_EEPROM_I2C_Read와 같은 기능 이름을 일반 HAL 형태의 래퍼보다 선호합니다.

BSP에는 다음이 포함될 수 있습니다:

  • 주변 장치 시작, 열기, 중지, 다시 시작 및 오류 복구 로직
  • DMA 수신/송신 시작 및 다시 시작
  • 드라이버 로컬 버퍼 초기화, 링 버퍼 이동, 사용 중 플래그, 오류 플래그 및 오버플로 카운터
  • HAL ISR 콜백에서 모듈 또는 태스크 알림으로의 라우팅
  • EXTI 타임스탬프 캡처 및 최소 ISR 측 이벤트 기록
  • 기본 보드 핀 상태 및 보드별 하드웨어 활성화 시퀀싱

BSP에는 다음이 포함되어서는 안 됩니다:

  • 외부 장치 프로토콜 구문 분석, 프레임 체크섬, 명령 코드 또는 패킷 의미
  • 저장, 재설정, 온라인 전환, 교정 또는 승인 흐름과 같은 명령 시퀀싱
  • 비즈니스 페이로드 해석 또는 제품 상태 전환
  • 네트워크 정책, 매개변수 자가 치유 규칙 또는 외부 장치에 대한 복구 상태 머신

외부 칩의 경우 App -> Device/Driver -> port -> BSP -> HAL 종속성 방향을 선호합니다. port 계층은 보드 기능을 외부 장치에 맞게 조정하는 반면, BSP는 보드 수준으로 유지되고 프로토콜에 구애받지 않습니다.

보드 초기화 경계

보드 초기화를 또 다른 하드웨어 기능 계층이 아닌 조정으로 유지합니다.

정적 하드웨어 구성에 CubeMX MX_xxx_Init() 함수를 사용합니다. 이러한 생성된 초기화 함수를 호출한 다음 BSP 소프트웨어 상태를 초기화하기 위해 하나의 보드 수준 이니셜라이저를 선호합니다:

main/App
  -> BSP_BoardInit() 또는 BOARD_InitHardware()
      -> MX_GPIO_Init()
      -> MX_DMA_Init()
      -> MX_USARTx_UART_Init()
      -> BSP_xxx_InitState()

소규모 프로젝트의 경우 이 조정을 User/BSP/bsp_board.c에 배치합니다. 대규모 또는 다중 보드 프로젝트의 경우 별도의 User/Board 계층이 허용됩니다. 두 경우 모두 보드 이니셜라이저는 보드만 조립합니다. UART 전송, GPIO 쓰기, 장치 재설정, 센서 읽기 또는 외부 칩 명령과 같은 런타임 기능을 제공하지 않습니다.

각 특정 BSP 모듈이 MX_GPIO_Init() 또는 MX_DMA_Init()와 같은 전역 CubeMX 초기화 함수를 호출하도록 허용하지 마십시오. 이는 단일 모듈이 소유하는 것이 아닌 보드 전체 정적 구성 진입점입니다. 특정 BSP 모듈은 자체 소프트웨어 상태와 런타임 기능만 초기화해야 합니다:

BSP_xxx_InitState()
  -> 버퍼, 플래그, 카운터, 기본 출력 상태
BSP_xxx_Start/Open()
  -> DMA 수신 시작, 주변 장치 런타임 활성화, 태스크 알림
BSP_xxx_Stop/Restart()
  -> 런타임 종료 또는 오류 복구

라이프사이클 및 상태 소유권

정적 초기화를 런타임 시작과 분리합니다.

  • CubeMX MX_xxx_Init()는 주변 장치 하드웨어를 구성합니다.
  • BSP InitState 함수는 BSP가 소유한 소프트웨어 상태를 초기화합니다.
  • BSP Start 또는 Open 함수는 주변 장치를 애플리케이션 코드에서 사용할 수 있게 만듭니다.
  • BSP Stop 또는 Restart 함수는 런타임 비활성화, 오류 복구 및 DMA/UART 재시작을 처리합니다.
  • 장치 드라이버는 외부 장치 프로토콜 상태를 소유합니다.
  • App 태스크는 제품 동작과 조정을 소유합니다.

런타임 상태의 소유권을 분산시키지 마십시오. 각 상태 변수는 하나의 소유자가 있어야 합니다. UART RX 버퍼는 BSP 또는 장치 포트에 속하고, 프로토콜 파서 상태는 프로토콜 모듈에 속하며, 명령 대기 상태는 명령 또는 장치 태스크 모듈에 속하고, 진단 카운터는 진단 또는 소유 장치 컨텍스트에 속하며, 비즈니스 상태는 App에 속합니다.

네이밍 정책

호출자가 호출 수준을 식별할 수 있도록 프로젝트 표시 계층 접두사를 사용합니다. 매뉴얼이나 기존 라이브러리와 일치하는 대문자 계층 및 기술 약어를 선호합니다:

APP_EventPost(...)
BSP_UART_SendDMA(...)
DRV_FlashWrite(...)
MOD_FilterProcess(...)
OSAL_QueueSendFromISR(...)
SVC_TraceInfo(...)
UTIL_CRC16Calc(...)

기술 매뉴얼이나 일반적인 임베디드 용어에서 대문자로 표시되는 약어(예: UART, SPI, I2C, ADC, DMA, GPIO, PWM, CRC, RTOS, ISR, USB, CAN, IAP, APP, BSP, OSAL, HAL)에는 대문자를 사용합니다.

함수 이름이 매크로처럼 보이지 않도록 합니다. BSP_UART_SendDMA를 BSP_UART_SEND_DMA보다 선호합니다. 매크로, 상수, 열거형 값 및 컴파일 타임 구성을 위해 밑줄이 있는 전체 대문자를 사용합니다.

기존 프로젝트가 이미 일관된 네이밍 스타일을 사용하는 경우 로컬 일관성을 유지하고 제안된 마이그레이션은 별도로 설명합니다.

CubeMX 및 MDK

생성된 파일을 변경할 때는 USER CODE BEGIN/END 블록 안에 사용자 편집 내용을 넣습니다. main.c를 확장하는 대신 User 아래에 새 수작업 .c/.h 파일을 추가하는 것을 선호합니다.

Keil MDK 및 EIDE 프로젝트의 경우 물리적 .c 파일을 추가하는 것만으로는 충분하지 않습니다. 프로젝트 그룹/구성을 통해 빌드에 참여하는지 확인하고 Inc 폴더 및 User/Config에 대한 include 경로를 추가합니다.

RTOS 지침

FreeRTOS 프로젝트의 경우 책임, 타이밍 및 차단 동작별로 태스크를 설계합니다. 실시간 경로를 로깅, 저장, 통신 재시도 및 진단과 분리합니다.

프로젝트 요구 사항을 충족한다면 OS 경계로 CMSIS-RTOS를 선호합니다. 프로젝트에 안정적인 타임아웃 의미 체계, ISR 변형, 오류 코드, 정적 할당 정책, 베어메탈 호환성 또는 향후 Zephyr 마이그레이션이 필요한 경우에만 사용자 지정 OSAL을 추가합니다.

마이그레이션이 중요한 경우 App, Driver 또는 Module 코드에 기본 FreeRTOS API를 분산시키지 마십시오.

출력 형식

아키텍처를 설계할 때 다음 순서로 응답합니다:

  1. 가정 및 제약 조건.
  2. 권장 계층/파일 배치.
  3. 데이터 흐름 및 이벤트/태스크 모델.
  4. 주요 인터페이스 또는 의사 코드.
  5. 위험, 절충 및 검증 단계.

코드를 검토할 때 스타일 제안보다 구체적인 위험 및 파일/계층 위반을 먼저 제시합니다.

관련 스킬