面试踩坑实录:自称精通 STM32+FreeRTOS,基础题直接露怯

오늘 면접한 후보자의 이력서에는 "STM32 개발과 FreeRTOS 응용에 정통함"이라고 명시되어 있었지만, 기대와 달리 기초적인 임베디드 질문들에 답하지 못해 실력이 그대로 드러났습니다. 핵심 질문과 전문적인 해설을 정리하여 동료들에게 경고를, 신입들에게는 교훈을 전달하고자 합니다. 기초가 부실하면 아무리 화려한 이력서도 소용없습니다.

1. 치명적인 Q&A 기록 (전문 분석 포함)

1. 질문: 임베디드에서 const와 volatile의 핵심 차이?

답변: const는 수정할 수 없고 volatile은 수정 가능하다…

:cross_mark: 겉모습만 언급, 본질을 놓침
:white_check_mark: 전문 해설:
const는 컴파일러 수준에서 수정 불가를 보장하는 것이며, 상수 및 읽기 전용 변수를 제한하여 실수로 쓰기 방지에 사용됩니다.
volatile은 컴파일러의 최적화를 금지하기 위한 것으로, 변수가 컴파일러 판단으로 변경되지 않는 것으로 간주되어 읽기/쓰기 생략되는 것을 방지합니다. 주요 적용 상황은 다음과 같습니다:

  • 하드웨어 레지스터 접근 (예: volatile uint32_t *reg = (uint32_t*)0x40000000;, 하드웨어에 의해 레지스터 값이 변경될 수 있음)
  • 멀티쓰레드/태스크 공유 변수
  • 인터럽트 서비스 루틴(ISR)과 메인 프로그램이 공유하는 전역 변수
    보충 설명: const volatile uint32_t *reg는 하드웨어 읽기 전용 레지스터를 나타냅니다 (하드웨어가 쓰고 소프트웨어가 읽으며, 소프트웨어의 오류 수정 방지 및 컴파일러 최적화 금지).

2. 질문: 인터럽트 서비스 루틴(ISR)에서 왜 printf를 호출하면 안 되나요?

답변: 출력이 느려서…

:cross_mark: 표면적 현상만 언급, 근본적 위험 미제시
:white_check_mark: 완전한 해답:
ISR에서 printf 사용 시 발생하는 3가지 치명적인 문제는 단순히 "느리다"는 것보다 훨씬 심각합니다:

  1. 동적 메모리 할당 (malloc/free)을 유발할 수 있으며, 동적 메모리 함수는 재진입 불가이므로 ISR과 메인 프로그램 간 충돌로 메모리 오류 발생 가능
  2. 시스템 호출을 포함하여 컨텍스트 전환을 유발, ISR의 원자성(atomicity) 손상
  3. 실행 시간이 예측 불가능하여 임베디드 실시간성 위반, 고우선순위 인터럽트까지 차단 가능
    대안 방안 (실제 적용 가능):
  • 링 버퍼에 로그 캐싱, ISR은 쓰기만 수행하고 메인 루프에서 출력 처리
  • 플래그 비트 트리거, 메인 프로그램이 플래그 감지 후 출력 실행
  • 동적 메모리 사용을 피하는 경량 로그 컴포넌트 통합

3. 질문: ISR과 메인 프로그램 간 32비트 변수의 안전한 읽기/쓰기 방법?

답변: 뮤텍스 락을 사용하면 되지 않나요?

:cross_mark: 오해: ISR에서 뮤텍스 사용 시 데드락 발생 가능, FreeRTOS의 뮤텍스는 기본적으로 ISR 호출 지원 안 함
:white_check_mark: 표준 실무 방안:

  1. 인터럽트 비활성화: __disable_irq()로 인터럽트 비활성화 → 변수 읽기/쓰기 → __enable_irq()로 인터럽트 활성화 (간단하지만 인터럽트 비활성화 시간 조절 필요)
  2. 원자적 연산: ARM 내장 함수 __atomic_load_32()/__atomic_store_32() 사용, 하드웨어 수준에서 단일 읽기/쓰기 보장
  3. 단일 바이트 분할: 32비트 변수를 4개의 8비트 변수로 분할하여 바이트 단위로 처리 (8/16비트 코어 호환용, 32비트 코어는 생략 가능)
  4. 하드웨어 명령어 활용: Cortex-M 아키텍처의 LDREX/STREX 명령어로 독점 접근 구현 (경쟁 상태 방지)
    주의: ARM 아키텍처에서 32비트 변수가 정렬되지 않게 접근하면 HardFault 인터럽트가 발생하므로 주의해야 합니다.

4. 질문: DMA와 인터럽트의 우선순위는 어떻게 처리해야 하나요?

답변: DMA 우선순위가 더 높아야 하지 않나요?

:cross_mark: 맹목적 추측, 3단계 우선순위 메커니즘 이해 부족
:white_check_mark: 심층 분석:
임베디드 시스템에서 DMA와 인터럽트의 우선순위는 단순히 "누가 더 높은지"가 아니라 다음 3단계 메커니즘을 종합적으로 고려해야 합니다:

  1. 하드웨어 우선순위: NVIC 컨트롤러 설정 (IRQn 우선순위, 수치가 작을수록 우선순위 높음)
  2. 소프트웨어 우선순위: RTOS 사용 시 태스크/인터럽트 서비스 함수의 소프트웨어 우선순위 (예: FreeRTOS vTaskPrioritySet)
  3. 데이터 스트림 우선순위: STM32의 DMA_CCRx 레지스터로 동일 DMA 채널 내 다른 데이터 스트림 우선순위 설정 가능
    실무 사례:
    ADC 샘플링에 DMA와 듀얼 버퍼를 사용할 때, DMA 인터럽트 우선순위가 ADC 인터럽트보다 높아야 합니다. DMA가 버퍼 전환을 완료하기 전에 ADC 인터럽트가 발생해 데이터 손실이 발생할 수 있기 때문입니다.

2. 면접 실패 사례 요약 (신입/HR/면접관 모두에게 적용)

1. 이력서의 함정 (기재하면 반드시 깊이 질문받는 항목)

  • "정통하다"는 표현은 신중하게: "STM32 정통"이라고 기재하면 SPI CRC 검증 구현, I2C 응답 타임아웃 처리, 타이머 데드존(dead zone) 구성 등 세부 사항을 반드시 묻습니다. "FreeRTOS 정통"이라고 하면 태스크 스케줄링 메커니즘, 세마포어와 뮤텍스 차이, 스택 오버플로우 디버깅 방법을 반드시 질문합니다.
  • 프로젝트 경험은 포괄적으로 적지 마세요: 단순히 "XXX 드라이버 개발 담당"이라고만 쓰지 말고 실제 작업 증빙을 포함하세요 (예: “디버깅 중 I2C 버스 경쟁 상태 발생, 로직 분석기로 파형 캡처 후 뮤텍스 락과 타임아웃 재전송으로 해결”)
  • 기술 매칭은 현실적으로: 라이브러리 함수 사용 가능하다고 해서 "정통하다"라고 단언하지 마세요. 저수준 드라이버를 독립적으로 작성하고 하드웨어-소프트웨어 결합 버그를 해결할 수 있어야 기준에 부합합니다.

2. 임베디드 면접 “죽음의 질문 목록” (답변 실패 시 면접 탈락 확률 높음)

  1. 프로젝트에서 인터럽트 처리 흐름도를 그려보세요 (컨텍스트 저장/복구, 인터럽트 중첩 로직 포함)
  2. 작성한 드라이버에 경쟁 상태(race condition)나 데드락 위험이 없는지 어떻게 검증했나요? (실제 작업 사례 필요, 예: 로직 분석기로 파형 캡처, 태스크 스택 상태 출력)
  3. 프로젝트에서 가장 힘들었던 하드웨어 버그 사례와 해결 과정을 설명하세요 (문제 진단 능력 평가)
  4. FreeRTOS에서 태스크 전환의 저수준 원리와 Cortex-M의 PendSV 인터럽트 역할은 무엇인가요?
  5. STM32에서 GPIO를 복합 기능으로 설정할 때 주의해야 할 사항은 무엇인가요? (예: 풀업/풀다운 저항, 출력 속도, AF 매핑)

3. 상호 교류

여러분이 면접 보면서 “이력서와 실력이 심각하게 불일치하는” 사례를 겪어본 적이 있거나, 임베디드 면접 핵심 질문을 보완하고 싶으시면 댓글 섹션에서 자유롭게 공유해 주세요. 신입 개발자분들께서는 기초를 소홀히 하지 마시고, "정통하다"라는 표현을 무분별하게 나열하지 마시고 확실한 기본기를 갖추는 것이 중요합니다!