임베디드 시스템에서 특정 주기로 LED를 깜빡이거나 센서 값을 읽어야 할 때, 매번 태스크(Task)를 만들어 osDelay()를 쓰는 것은 리소스 낭비가 될 수 있습니다. 이때 가장 효율적인 대안이 바로 소프트웨어 타이머(Software Timer)입니다.
이번 포스팅에서는 CMSIS-RTOS v2 표준 API를 사용하여 소프트웨어 타이머를 생성하고 관리하는 방법과 실무 예제를 자세히 알아보겠습니다.

1. 소프트웨어 타이머란?
소프트웨어 타이머는 설정한 시간이 경과했을 때 특정 콜백 함수(Callback Function)를 자동으로 실행해 주는 메커니즘입니다. 하드웨어 타이머와 달리 RTOS 커널에 의해 소프트웨어적으로 관리되므로 하드웨어 자원을 아낄 수 있습니다.
핵심 특징
- 비차단(Non-blocking) 동작: 타이머는 백그라운드에서 시간을 계산하며 메인 루프를 방해하지 않습니다.
- 모드 선택: 한 번만 실행되는 단발성(One-shot)과 일정한 간격으로 반복되는 주기적(Periodic) 모드를 지원합니다.
- 리소스 효율성: 여러 개의 타이머를 생성하더라도 하나의 타이머 서비스 태스크에서 관리하므로 메모리 효율이 좋습니다.
2. 핵심 API 사용법 (CMSIS-RTOS v2)
2.1 osTimerNew(): 타이머 생성
새로운 타이머 객체를 생성하고 콜백 함수를 등록합니다.
osTimerId_t osTimerNew(osTimerFunc_t func, osTimerType_t type, void *argument, const osTimerAttr_t *attr);
- func: 타이머 만료 시 실행될 함수 주소
- type: osTimerOnce(단발성) 또는 osTimerPeriodic(주기적) 설정
- argument: 콜백 함수에 전달할 인자
2.2 osTimerStart(): 타이머 시작
생성된 타이머를 가동합니다.
osStatus_t osTimerStart(osTimerId_t timer_id, uint32_t ticks);
- ticks: 만료 시간 (시스템 틱 단위, 보통 1ms 당 1틱 설정 시 500은 500ms)
3. 실전 활용 예제
예제 1: 500ms 주기 LED 점멸 제어
태스크를 별도로 생성하지 않고도 타이머만으로 간단하게 LED를 제어할 수 있습니다.
#include "cmsis_os2.h"
// 1. 타이머 콜백 함수 정의
void LED_Callback(void *argument) {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // LED 토글
}
int main(void) {
osKernelInitialize();
// 2. 주기적 타이머 생성 (500ms 간격)
osTimerId_t led_timer = osTimerNew(LED_Callback, osTimerPeriodic, NULL, NULL);
if (led_timer != NULL) {
osTimerStart(led_timer, 500); // 타이머 시작
}
osKernelStart();
while (1);
}
예제 2: 1초 주기 센서 데이터 업데이트
센서 데이터를 정기적으로 폴링(Polling)해야 하는 경우에 적합합니다.
void Sensor_Callback(void *argument) {
float data = Read_Sensor_Value(); // 센서 읽기 가상 함수
Update_System_Status(data); // 데이터 처리
}
// 메인 함수 내 설정
osTimerId_t sensor_timer = osTimerNew(Sensor_Callback, osTimerPeriodic, NULL, NULL);
osTimerStart(sensor_timer, 1000); // 1000ms 주기로 동작
4. [중요] 개발자 주의사항 (콜백 함수의 제약)
소프트웨어 타이머의 콜백 함수는 'Timer Service Task'라는 공유 태스크 내에서 실행됩니다. 따라서 다음과 같은 주의사항을 반드시 지켜야 합니다.
- 지연 함수 사용 금지: 콜백 함수 내부에서 osDelay()나 무한 루프를 사용하면 안 됩니다. 시스템 전체의 타이머 서비스가 멈출 수 있습니다.
- 블로킹 API 피하기: 세마포어 대기(osWaitForever)와 같이 태스크를 멈추게 하는 API 호출을 자제해야 합니다.
- 최대한 짧게 작성: 콜백 함수는 최소한의 로직(플래그 설정, 간단한 IO 제어)만 수행하고 즉시 반환해야 합니다.
결론
CMSIS-RTOS v2의 소프트웨어 타이머는 복잡한 주기적 작업을 간결한 코드로 해결해 주는 도구입니다. 오늘 배운 osTimerNew와 주의사항을 바탕으로 여러분의 임베디드 프로젝트에 효율적인 시간 관리 기능을 구현해 보세요!
글이 도움이 되셨다면 공감과 댓글 부탁드립니다! 여러분은 주기적 작업을 태스크와 타이머 중 어떤 방식으로 구현하시나요?
'Firmware & RTOS > FreeRTOS & Real-time Scheduling' 카테고리의 다른 글
| FreeRTOS 이벤트 그룹(Event Group) 완벽 가이드: 다중 태스크 동기화 (CMSIS-RTOS v2) (0) | 2025.01.17 |
|---|---|
| FreeRTOS 저전력 최적화 가이드: Idle Task와 Tickless Idle 모드 활용법 (0) | 2025.01.16 |
| FreeRTOS 세마포어(Semaphore) vs 뮤텍스(Mutex) 차이점과 올바른 사용법 (0) | 2025.01.14 |
| FreeRTOS 큐(Queue) 완벽 가이드: 태스크 간 데이터 통신 및 예제 (CMSIS-RTOS v2) (0) | 2025.01.13 |
| CMSIS-RTOS v2 태스크 관리 완벽 가이드: 생성부터 상태 전환까지 (0) | 2025.01.12 |