Files
firmware-test/project/ble_peripheral/ble_app_bladder_patch/led_control.c
2026-04-02 11:17:06 +09:00

293 lines
8.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*******************************************************************************
* @file led_control.c
* @brief LED 직접 제어 드라이버 (BSP 미사용)
* @date 2026-03-30
*
* app_timer 1개로 2색 LED(녹색/주황)의 복합 blink 패턴 구현
* 단순 on/off 상태는 타이머 없이 즉시 GPIO 제어
* 복합 패턴(에러 등)은 phase 기반 state machine으로 처리
******************************************************************************/
#include "led_control.h"
#include "nrf_gpio.h"
#include "app_timer.h"
/*==============================================================================
* 내부 상수
*============================================================================*/
#define MS_TO_TICKS(ms) APP_TIMER_TICKS(ms)
/*==============================================================================
* 색상
*============================================================================*/
#define COLOR_NONE 0
#define COLOR_GREEN 1
#define COLOR_ORANGE 2
/*==============================================================================
* 에러 패턴 상수 (No.7)
* 3Hz 깜빡 3회 = 166ms on + 166ms off × 3 = ~1초, 이후 꺼짐 1초
*============================================================================*/
#define ERROR_BLINK_ON_MS 166
#define ERROR_BLINK_OFF_MS 166
#define ERROR_BLINK_COUNT 3
#define ERROR_PAUSE_MS 1000
/*==============================================================================
* 패턴 테이블 (단순 blink 용)
*============================================================================*/
typedef struct
{
uint32_t on_ms; /* LED 켜짐 시간 (ms) */
uint32_t off_ms; /* LED 꺼짐 시간 (ms) */
uint8_t color; /* COLOR_GREEN 또는 COLOR_ORANGE */
bool repeat; /* true: 무한 반복, false: 1회 후 OFF */
} led_pattern_t;
static const led_pattern_t m_patterns[LED_STATE_COUNT] =
{
[LED_STATE_OFF] = { 0, 0, COLOR_NONE, false },
[LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → 유지 */
[LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, /* 초록 점등 2초 → OFF */
[LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, /* 초록 점멸 1초 */
[LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, /* 초록 1초 on / 3초 off */
[LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, /* 주황 점멸 1초 */
[LED_STATE_ALIGN_COMPLETE] = { 3000, 1000, COLOR_GREEN, true }, /* 초록 3초 on / 1초 off */
[LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true } /* 별도 state machine */
};
/*==============================================================================
* 모듈 변수
*============================================================================*/
APP_TIMER_DEF(m_led_timer);
static led_state_t m_current_state = LED_STATE_OFF;
static bool m_phase_on; /* true: LED 켜진 구간, false: 꺼진 구간 */
/* 에러 패턴 전용 */
static uint8_t m_error_blink_cnt; /* 현재까지 깜빡인 횟수 */
static uint8_t m_error_phase; /* 0: blink-on, 1: blink-off, 2: pause */
/*==============================================================================
* GPIO 헬퍼
*============================================================================*/
static inline void led_green_on(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_clear(LED_PIN_GREEN);
#else
nrf_gpio_pin_set(LED_PIN_GREEN);
#endif
}
static inline void led_green_off(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_set(LED_PIN_GREEN);
#else
nrf_gpio_pin_clear(LED_PIN_GREEN);
#endif
}
static inline void led_orange_on(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_clear(LED_PIN_ORANGE);
#else
nrf_gpio_pin_set(LED_PIN_ORANGE);
#endif
}
static inline void led_orange_off(void)
{
#if LED_ACTIVE_LOW
nrf_gpio_pin_set(LED_PIN_ORANGE);
#else
nrf_gpio_pin_clear(LED_PIN_ORANGE);
#endif
}
static void led_all_off(void)
{
led_green_off();
led_orange_off();
}
static void led_color_on(uint8_t color)
{
led_all_off();
if (color == COLOR_GREEN) led_green_on();
else if (color == COLOR_ORANGE) led_orange_on();
}
/*==============================================================================
* 타이머 시작 헬퍼
*============================================================================*/
static void timer_start_ms(uint32_t ms)
{
if (ms == 0) return;
app_timer_start(m_led_timer, MS_TO_TICKS(ms), NULL);
}
/*==============================================================================
* 에러 패턴 state machine (No.7)
* phase 0: LED ON (166ms) → phase 1
* phase 1: LED OFF (166ms) → cnt++ → cnt<3 ? phase 0 : phase 2
* phase 2: PAUSE (1000ms) → cnt=0, phase 0
*============================================================================*/
static void error_pattern_start(void)
{
m_error_blink_cnt = 0;
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
timer_start_ms(ERROR_BLINK_ON_MS);
}
static void error_pattern_tick(void)
{
switch (m_error_phase)
{
case 0: /* ON 구간 끝 → OFF */
led_all_off();
m_error_phase = 1;
timer_start_ms(ERROR_BLINK_OFF_MS);
break;
case 1: /* OFF 구간 끝 */
m_error_blink_cnt++;
if (m_error_blink_cnt < ERROR_BLINK_COUNT)
{
/* 다시 ON */
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
timer_start_ms(ERROR_BLINK_ON_MS);
}
else
{
/* 3회 완료 → pause */
m_error_phase = 2;
timer_start_ms(ERROR_PAUSE_MS);
}
break;
case 2: /* pause 끝 → 처음부터 */
m_error_blink_cnt = 0;
m_error_phase = 0;
led_color_on(COLOR_ORANGE);
timer_start_ms(ERROR_BLINK_ON_MS);
break;
default:
break;
}
}
/*==============================================================================
* 타이머 콜백
*============================================================================*/
static void led_timer_handler(void * p_context)
{
(void)p_context;
/* 에러 상태는 별도 처리 */
if (m_current_state == LED_STATE_ERROR)
{
error_pattern_tick();
return;
}
const led_pattern_t * p = &m_patterns[m_current_state];
if (m_phase_on)
{
/* ON→OFF 전환 */
led_all_off();
m_phase_on = false;
if (p->off_ms > 0)
{
timer_start_ms(p->off_ms);
}
else if (!p->repeat)
{
/* 1회성: off_ms == 0 이면 그냥 유지 (POWER_ON) 또는 끄기 (POWER_OFF) */
if (m_current_state == LED_STATE_POWER_OFF)
{
led_all_off();
m_current_state = LED_STATE_OFF;
}
/* POWER_ON: 점등 유지 상태 → 타이머 x */
}
}
else
{
/* OFF → ON 전환 */
if (p->repeat)
{
led_color_on(p->color);
m_phase_on = true;
timer_start_ms(p->on_ms);
}
/* repeat == false && off_ms > 0 인 경우는 현재 없음 */
}
}
/*==============================================================================
* 공개 함수
*============================================================================*/
void led_init(void)
{
/* GPIO 출력 설정 */
nrf_gpio_cfg_output(LED_PIN_GREEN);
nrf_gpio_cfg_output(LED_PIN_ORANGE);
led_all_off();
/* 타이머 생성 (single-shot) */
app_timer_create(&m_led_timer, APP_TIMER_MODE_SINGLE_SHOT, led_timer_handler);
m_current_state = LED_STATE_OFF;
}
void led_set_state(led_state_t state)
{
if (state >= LED_STATE_COUNT) return;
/* 이전 패턴 중단 */
app_timer_stop(m_led_timer);
led_all_off();
m_current_state = state;
m_phase_on = false;
const led_pattern_t * p = &m_patterns[state];
switch (state)
{
case LED_STATE_OFF:
/* 이미 all off */
break;
/* 에러 패턴: 별도 state machine */
case LED_STATE_ERROR:
error_pattern_start();
break;
/* 그 외: on 구간부터 시작 */
default:
led_color_on(p->color);
m_phase_on = true;
if (p->on_ms > 0)
{
timer_start_ms(p->on_ms);
}
break;
}
}
led_state_t led_get_state(void)
{
return m_current_state;
}