STM32CubeMX는 STM32 마이크로컨트롤러용 프로젝트를 쉽게 생성할 수 있는 강력한 도구입니다.
하지만 기본적으로 CubeMX가 생성하는 코드는 main.c
안에 모든 초기화 코드와 주변장치 핸들러가 몰려 있어 프로젝트가 커질수록 관리가 어려워집니다.
아두이노처럼 setup()
와 loop()
구조를 적용하면, 사용자 코드를 CubeMX 자동 생성 코드와 분리하여 깔끔하게 관리할 수 있습니다.
이번 글에서는 CubeMX에서 아두이노 스타일 코드 구조를 만들고, 전역 변수와 USART 핸들러를 안전하게 사용하는 방법까지 소개합니다.
1. 기본 CubeMX 구조
CubeMX로 USART 하나를 활성화하고 프로젝트를 생성하면, main.c
는 대략 다음과 같이 구성됩니다:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
while (1)
{
// 사용자 코드 작성
}
}
사용자 코드를 while(1)
안에 직접 작성하면, CubeMX가 다시 코드를 생성할 때 기존 코드와 충돌이 발생할 수 있습니다.
이를 해결하기 위해, 사용자 전용 파일을 만들어 setup()
과 loop()
함수를 정의하면, main.c는 초기화와 함수 호출만 담당하게 만들 수 있습니다.
2. 아두이노 스타일 코드 구조
프로젝트 구조 예시
Core/
├── Inc/
│ └── user_app.h ← 사용자 헤더 (전역 변수 extern 선언)
├── Src/
│ ├── user_app.c ← 사용자 코드 (전역 변수 정의, setup()/loop() 함수)
│ └── main.c ← CubeMX 자동 생성 (setup()/loop() 호출만)
user_app.h
#ifndef __USER_APP_H
#define __USER_APP_H
#include "main.h"
// 사용자 전역 변수 extern 선언
extern int counter;
extern float sensor_value;
// 사용자 함수 선언
void setup();
void loop();
#endif
user_app.c
#include "user_app.h"
// 사용자 전역 변수 정의
int counter = 0;
float sensor_value = 0.0f;
void setup() {
counter = 0;
sensor_value = 0.0f;
char msg[] = "System Initialized!\r\n";
HAL_UART_Transmit(&huart1, (uint8_t*)msg, sizeof(msg)-1, HAL_MAX_DELAY);
}
void loop() {
counter++;
sensor_value = counter * 0.1f;
char buf[50];
int len = sprintf(buf, "Counter=%d, Sensor=%.2f\r\n", counter, sensor_value);
HAL_UART_Transmit(&huart1, (uint8_t*)buf, len, HAL_MAX_DELAY);
HAL_Delay(1000); // 1초마다 반복
}
main.c
#include "user_app.h"
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
setup(); // setup 호출
while (1)
{
loop(); // loop 호출
}
}
장점
- main.c는 CubeMX가 다시 생성해도 안전
- 사용자 코드는 user_app.c/h 안에서만 관리
- Arduino 스타일
setup()
/loop()
패턴 적용 가능
3. 전역 변수 관리 (extern)
여러 C 파일에서 같은 변수를 사용하려면 extern
키워드를 활용합니다.
- 정의(Definition): 실제 메모리 공간 생성 → .c 파일에서 한 번만 선언
- 선언(Declaration): 다른 파일에서 참조할 수 있도록 이름만 알림 → 헤더(.h)에서 extern 사용
// user_app.c
int counter = 0; // 정의
// user_app.h
extern int counter; // 선언
// main.c
#include "user_app.h"
if (counter % 10 == 0) {
// counter 사용 가능
}
4. USART 핸들러(huart1) 사용법
USART 핸들러는 CubeMX 옵션에 따라 정의 위치가 달라집니다.
4.1 Default Mode
- main.c →
UART_HandleTypeDef huart1;
정의 - main.h →
extern UART_HandleTypeDef huart1;
선언 - 사용자 코드 →
#include "main.h"
4.2 Separate Files Mode
- usart.c →
UART_HandleTypeDef huart1;
정의 - usart.h →
extern UART_HandleTypeDef huart1;
선언 - 사용자 코드 →
#include "usart.h"
CubeMX 옵션 | 정의 위치 | extern 선언 위치 | 사용자 코드에서 include |
Default Mode | main.c | main.h | #include "main.h" |
Separate Files Mode | usart.c | usart.h | #include "usart.h" |
5. CubeMX 아두이노 스타일 사용 장점
- 코드 관리가 쉽고, main.c를 건드리지 않아도 됨
- 전역 변수, UART, GPIO 등 사용자 자원을 한 곳에서 관리 가능
- Arduino 경험이 있는 사용자에게 친숙한 개발 패턴 적용 가능
- CubeMX 자동 생성 코드와 충돌 최소화
'MCU > STM32' 카테고리의 다른 글
ADXL345 가속도 센서 STM32 HAL 라이브러리 드라이버 코드 구현 (0) | 2025.08.24 |
---|---|
STM32 ADC 샘플 타임 설정 방법과 실무 팁 (0) | 2025.08.21 |
STM32 에서 PWM Soft Start 구현: 모터와 LED 제어를 위한 부드러운 시작 (0) | 2025.08.21 |
STM32 링커 스크립트: 상세 설명, 구조, 작성 방법 및 예제 (STM32 Linker Script: Detailed Explanation, Structure, Writing Methods, and Examples) (4) | 2025.08.18 |
STM32로 Modbus RTU Slave 코드 구현: DMA와 저전력 최적화 (0) | 2025.08.13 |
STM32G474 를 이용한 동일 어드레스를 갖는 64채널 I2C 디바이스 제어 (0) | 2025.08.12 |
STM32H503RB I3C 통신 코드 구현: 초보자를 위한 상세 가이드 (0) | 2025.08.10 |