2026년 10월 21일날씨: 맑음
지난 5월, STSPIN32G4로 BLDC 모터를 FOC(자속 기준 제어)로 돌리는 과정을 짧은 영상 세 편으로 남겼다. Motor Control Workbench로 프로젝트를 만드는 전체 흐름, 생성된 drive_parameters.h에서 실제로 손대게 되는 항목, 그리고 Motor Pilot의 프로파일러로 모터 시정수를 재는 방법이다. 화면 녹화라 말로 풀지 못한 부분이 많았다. 그때 느낀 점과 몇 달 더 써 보며 알게 된 것들을 여기에 모아 둔다.

왜 STSPIN32G4인가
예전에는 BLDC를 돌리려면 홀 센서나 엔코더를 읽고, 타이머 인터럽트에서 전류(轉流) 순서를 바꾸고, 가감속 궤적을 직접 계산했다. 되기는 되지만 모터를 바꿀 때마다 그 코드를 다시 손봐야 했고, 센서리스나 FOC 같은 것은 엄두를 내지 못했다. 영상 설명에 “언제까지 타이머 돌려서 경로 궤적 구하고 엔코더 읽고 예전 방식으로 할 것인가”라고 쓴 것은 그 답답함이었다.
STSPIN32G4는 STM32G431(Cortex-M4, 170 MHz) 코어와 3상 게이트 드라이버, 전원 레귤레이터를 한 패키지에 넣은 칩이다. 바깥에 MOSFET 여섯 개와 션트 저항 세 개만 붙이면 3상 인버터가 된다. 여기에 ST가 무료로 주는 Motor Control SDK(MCSDK)를 쓰면 전류 루프, 속도 루프, 위치 루프, 센서리스 관측기까지 이미 들어 있는 펌웨어를 설정만으로 받아 쓸 수 있다. 모터 드라이버를 “짜는” 일이 “설정하고 다듬는” 일로 바뀐다. 쓰는 도구는 다섯 가지다.
| 도구 | 역할 |
|---|---|
| MotorControl Workbench 6.4.x | 보드, 모터, 제어 방식을 고르고 펌웨어 프로젝트를 생성한다 |
| STM32CubeMX | Workbench가 넘겨준 .ioc로 핀과 주변장치를 확정하고 코드를 생성한다 |
| IAR Embedded Workbench (또는 STM32CubeIDE) | 빌드하고 ST-LINK로 다운로드, 디버그한다 |
| Motor Pilot | UART로 보드와 연결해 기동과 정지, 속도 지령, 레지스터 읽기와 쓰기, 파형 보기 |
| Motor Profiler (Motor Pilot 안) | 모터를 실제로 돌려 Rs, Ls, Ke, 관성, 마찰을 측정한다 |
영상 1. Workbench로 설정하기, 전체 흐름
7분짜리 영상이지만 흐름은 단순하다. 보드와 모터를 고르고, 요약 화면에서 세부 설정을 확인하고, 프로젝트를 생성해 빌드하면 끝이다. 영상에 나온 순서대로 적으면 이렇다.
| 단계 | 화면 | 내용 |
|---|---|---|
| 1 | Workbench 홈, BOARD | 보드 목록에서 EVSPIN32G4를 고른다. 170 MHz, 버스 10~75 V, 정격 20 A. |
| 2 | MOTOR | SM-PMSM 24 V 모터. 극쌍 수 2, 최대 4 krpm, 정격 1.8 Apk. 실제 모터 값은 여기서 바꾸거나, 나중에 프로파일러로 잰 값으로 고친다. |
| 3 | Go To Summary | FOC 블록도가 나온다. 전원 24 Vdc, 구동 모드 Speed, PWM 16 kHz(뒤에 20 kHz로), 버스 전압 감지 10~28.8 V, 온도 100 ℃, 전류 감지 45 Apk, 속도 감지 Observer+PLL(센서리스). |
| 4 | FOC Wizard | 블록을 누르면 세부 설정. Motor(Rs, Ls, B-EMF 상수, 관성, 마찰, 최대 응용 속도), PWM Generation(주파수, 데드타임, DRV1 핀 매핑 GHS/GLS), Current Sensing(3션트, 내부 OPAMP 7.33배, 샘플링 시간), Speed Sensing Config(센서리스 기동 프로파일, 기동 종료 조건, Rev-up에서 FOC 전환), Driver Protection, Drive Settings, Application Configuration(FreeRTOS, DAC Debug, Motor Profiler). |
| 5 | Save, Generate the project | STM32CubeMX 6.13과 X-CUBE-MCSDK 6.4.2로 생성한다. 툴체인은 IAR EWARM. |
| 6 | STM32CubeMX | 생성된 .ioc를 열면 STM32G431VBTx LQFP100 핀 배치가 나온다. UART, I2C 같은 추가 주변장치는 여기서 붙이고 Generate Code. |
| 7 | IAR | 구버전 프로젝트 변환을 수락하고, Options에서 최적화와 디버거(ST-LINK, SWD)를 맞춘 뒤 Build, Download and Debug. while 루프에 테스트 카운터를 넣고 Live Watch로 도는지 본다. |
영상 중간에 ST-LINK가 “No MCU device found”를 내는 장면이 있다. 보드 전원과 SWD 배선을 다시 확인하고 재시도하면 붙는다. 처음 한 번은 꼭 겪는 일이라 일부러 잘라내지 않았다.
Workbench는 코드 생성기이지 편집기가 아니다. Workbench에서 바꾼 설정은 전부 헤더 세 개로 떨어진다. 모터 값은
pmsm_motor_parameters.h, 보드 쪽(션트, 증폭, 전압 분압, 데드타임)은power_stage_parameters.h, 제어 루프와 보호와 기동은drive_parameters.h다. 이걸 알고 나면 소소한 튜닝은 Workbench를 다시 열지 않고 헤더만 고치면 된다. 다만 Workbench에서 다시 생성하면 그 수정이 덮어써진다. 오래 갈 값은 Workbench(.stwb6)와 CubeMX(.ioc)에도 같이 맞춰 두거나 USER CODE 영역에 적어 둬야 한다. 한 번 당하고 나서야 알았다.
영상 2. drive_parameters.h, 실제로 손대는 곳

이 무렵 BLDC로 위치 제어를 하고 있었다. 프로파일러로 모터 값을 맞추고 PI 게인을 튜닝한 다음에도, 쓰다 보면 반복해서 고치는 항목은 셋이었다. AD 샘플링 시간, 최대 회전수, 그리고 헌팅이 심할 때 바꿔 보는 PWM 캐리어 주파수다. 영상은 그 파일의 어디에 무엇이 있는지를 위에서부터 훑은 것이다. 영상에 보이는 값과 함께 정리한다.
| 구역 | 주요 항목 | 뜻, 그리고 고칠 때 |
|---|---|---|
| 속도 센서 설정 | MAX_APPLICATION_SPEED_RPM, MIN_APPLICATION_SPEED_RPM, M1_SS_MEAS_ERRORS_BEFORE_FAULTS | 응용에서 쓰는 최대, 최소 회전수. 관측기는 이 범위로 속도 추정의 신뢰 구간을 잡는다. 속도 추정 오류가 정해진 횟수를 넘으면 Speed feedback fault가 난다. |
| 상태 관측기와 PLL | VARIANCE_THRESHOLD, F1, F2, GAIN1, GAIN2, PLL_KP_GAIN, PLL_KI_GAIN, STO_FIFO_DEPTH | 센서리스 속도 추정기의 게인. Workbench가 Rs, Ls, 최대 속도로 계산해 넣으므로 직접 고칠 일은 드물다. 모터 값이 틀리면 여기가 흔들린다. |
| PWM 생성 | PWM_FREQUENCY 25000, SW_DEADTIME_NS 750, REGULATION_EXECUTION_RATE 1 | 캐리어 주파수(Hz)와 데드타임. 전류 루프는 PWM 주기마다 한 번 돈다. 헌팅이 심하거나 소음이 거슬리면 캐리어 주파수를 바꿔 본다. 올리면 전류 리플은 줄지만 스위칭 손실과 인터럽트 부담이 는다. |
| 토크, 자속 루프 | PID_TORQUE_KP/KI/KD 3773/3460/100, PID_FLUX_KP/KI/KD, TF_KPDIV 2048, TF_KIDIV 8192 | Iq, Id 전류 PI. Rs, Ls로 설계된다. 고정소수점이라 KP를 KPDIV로 나눈 값이 실제 게인이다. |
| 속도 루프 | PID_SPEED_KP/KI/KD 3939/2191/0, SPEED_LOOP_FREQUENCY_HZ 1000, SP_KPDIV 128, SP_KIDIV 8192 | 속도 PI, 1 kHz로 실행. 거칠게 돌거나 Speed feedback이 잦으면 Kp를 낮춘다. |
| 위치 루프 | POSITION_LOOP_FREQUENCY_HZ 1000, PID_POSITION_KP/KI/KD 4000/100/200, PID_POSITION_ANGLE_STEP, PID_POSITION_MOV_DURATION | 위치 제어 PID. 각도 스텝과 이동 시간은 Pilot에서 위치 명령을 보낼 때의 기본값. |
| 전류 한도 | IQMAX_A | 토크 전류 상한. 낮으면 회전 토크가 모자라 기동을 못 한다. NOMINAL_CURRENT_A와 맞춘다. 영상에서는 2 A를 두고 주석에 4 A를 적어 두었다. |
| 기본 동작 | DEFAULT_CONTROL_MODE, DEFAULT_TARGET_SPEED_RPM 850 | 전원을 켜고 Start 했을 때의 모드(Speed)와 목표 속도. |
| 보호 | OV_VOLTAGE_THRESHOLD_V 28, UD_VOLTAGE_THRESHOLD_V 10, OV_TEMPERATURE_THRESHOLD_C 70 | 과전압, 저전압, 과열 한계. 24 V 전원에 맞춘 값이다. |
| 기동(센서리스) | M1_ALIGNMENT_DURATION 700 ms, M1_ALIGNMENT_ANGLE_DEG 90, FINAL_I_ALIGNMENT_A, OBS_MINIMUM_SPEED_RPM 1000, SPEED_BAND_UPPER_LIMIT 17, SPEED_BAND_LOWER_LIMIT 15, TRANSITION_DURATION 25 ms | 정렬, 강제 가속(rev-up)을 거쳐 관측기 추정 속도가 1000 rpm을 넘고 오차 띠 안에 들면 25 ms 동안 FOC 폐루프로 넘어간다. Start Up Failure는 대개 이 구역과 전류 한도 문제다. |
| ADC | M1_VBUS_SAMPLING_TIME LL_ADC_SAMPLING_CYCLE(47), ADC_SAMPLING_CYCLES (6 + SAMPLING_CYCLE_CORRECTION) | 버스 전압과 상전류의 AD 샘플링 시간(클록 수). 전류 샘플링은 PWM 가운데의 짧은 창에서 이뤄지므로 길게 잡으면 쓸 수 있는 최대 듀티가 줄고, 짧게 잡으면 노이즈를 탄다. |
영상 끝에는 main.c도 잠깐 나온다. 부트로더를 쓰려고 SCB->VTOR로 벡터 테이블을 옮기는 세 줄, 그리고 초기화 순서다. ADC, 비교기(COMP), CORDIC, DAC3(과전류 기준 전압), OPAMP, TIM1을 먼저 올리고 그다음에 MX_MotorControl_Init()을 불러야 한다. 순서가 틀리면 모터 제어가 아예 뜨지 않는다. Motor Pilot이 쓰는 USART1은 그대로 두고, 상위 장치와 주고받는 명령 프로토콜은 USART3에 따로 붙였다.
영상 3. Motor Profiler로 모터 시정수 재기
FOC의 품질은 결국 모터 모델 값이 얼마나 맞느냐에 달려 있다. 센서리스 관측기는 “이 전압을 걸었더니 이 전류가 흘렀으니 역기전력은 이만큼일 것이다”라고 Rs, Ls로 역산하는 장치다. 데이터시트 값이 없거나 믿기 어려운 모터라면 프로파일러로 직접 재는 것이 가장 빠르다. 순서는 이렇다.
- Motor Pilot을 열고 Load UI로 프로파일러 화면을 불러온다. 이때 FOC 모드로 들어간다.
- 펌웨어의 UART 속도와 Pilot의 연결 속도를 맞춘다. 너무 느리지만 않으면 된다.
- 연결되면 교정(calibration)을 할지 묻는다. 교정 또는 No를 고른다.
- 처음 붙으면 보통 “Firmware fault detected”가 빨갛게 뜬다. Ack Faults로 에러를 지운다.
- 극쌍 수(Pole pairs), 최대 속도, 최대 전류, 저전압과 과전압 한계(영상에서는 12 < 23 < 31 V), 자기 구조(SM-PMSM)를 넣고 Start profile을 누른다.
- 모터가 몇 단계로 돌면서 진행률이 올라간다. 100 %가 되면 Rs, Ls, Ke, 관성, 마찰이 채워지고, Save로 Workbench 모터 파일에 저장한다.

28 % 부근에서 진행이 멈춘 채 모터가 그냥 돌기만 하거나 정지해 있으면 타임아웃이다. 극쌍 수, 속도, 전류를 적당히 고쳐 다시 한다. 그래도 안 되면 소스의 Rs, Ls 같은 시정수를 조금 손봐야 한다.
영상 3의 자막에서
왜 그런가 하면, 프로파일러도 결국 같은 펌웨어 위에서 돈다. 처음 넣은 모터 값이 실제와 너무 다르면 관측기가 속도를 못 잡고, 프로파일러는 다음 단계로 못 넘어간다. 그래서 100 %가 안 되면 회로 쪽 시정수(션트 값, 증폭률, 버스 전압 분압이 실제 보드와 맞는지)와 모터 기본 파라미터(극쌍 수, 대략의 Rs와 Ls, 최대 속도)를 먼저 의심해야 한다. 특히 최대 속도를 실제보다 낮게 적으면 “The measurement is taking too much time”으로 멈춘다. 전기 시정수 τ = Ls/Rs가 수백 µs인 작은 모터는 AD 샘플링과 전류 루프가 그 안에서 따라가야 하므로 PWM 주파수와 샘플링 시간도 같이 본다.
직접 만든 보드로 옮기며 배운 것
EVSPIN32G4로 돌려 본 뒤 같은 칩으로 보드를 직접 그렸다. 참고 보드와 내 회로를 비교하며 정리한 체크리스트다.
| 항목 | 내용 |
|---|---|
| 션트 저항 | 3상 모두 10 mΩ으로 통일했다. 펌웨어의 RSHUNT, CubeMX의 M1_RSHUNT, Workbench 값이 서로 다르면 코드를 다시 생성할 때 되돌아간다. 세 곳을 같이 맞춘다. |
| SCREF 핀 | VDS 단락 보호의 기준 전압. 0.2~2.5 V면 보호가 켜지고 2.9~3.3 V면 꺼진다. 테스트 때 꺼 두고 잊으면 안 된다. |
| 속도 피드백 | Observer+PLL 센서리스만 쓰고 엔코더는 커넥터만 남겨 두었다. 나중에 쓰려면 타이머를 엔코더 모드로 잡고 Workbench의 Speed Sensing을 바꿔 다시 생성한다. |
| Speed feedback fault | 모터는 잘 도는데 fault만 뜨면 M1_SS_MEAS_ERRORS_BEFORE_FAULTS를 3에서 25까지 늘려 완화했다. 근본 원인은 Rs, Ls, B-EMF 값의 불일치다. |
| Pilot에서 속도 바꾸기 | Speed Reference 칸만 바꿔서는 보드로 가지 않는다. Target Speed와 Duration을 넣고 Execute Speed Ramp를 눌러야 한다. |
| 안 돌 때 | Pilot의 Status와 Errors부터 본다. Start Up Failure는 기동 구역과 전류 한도, Over Current는 배선과 드라이버, Under Voltage는 전원이다. 전류 한도를 잠시 10 A로 올려서 돌면 한도 문제이고, 그래도 안 돌면 모델, 배선, 전원 문제다. |
| 과온 | NTC를 달았으면 M1_TEMPERATURE_READING을 켜고 과온 때 PWM을 끄는 처리를 넣는다. |
정리
세 영상을 다시 보니 결론은 간단하다. 첫째, Workbench 흐름만 익히면 새 모터도 하루 안에 FOC로 돌릴 수 있다. 둘째, 그 뒤의 모든 튜닝은 헤더 세 개 안에서 끝난다. 셋째, 그 튜닝의 출발점은 프로파일러 100 %다. 모델이 틀리면 게인을 아무리 만져도 안정이 되지 않는다. 다음에는 엔코더를 붙여 위치 제어 정밀도를 올리고, 전류 한도와 발열, 상위 장치와의 명령 프로토콜을 정리한 글을 쓸 생각이다.