/** * button.c — 第二个 OOPC 组件实现 * * 展示关键 OOPC 设计点: * 1. 内部 FSM 状态机完全对外隐藏 * 2. 去抖动逻辑封装在内部 * 3. 通过回调与 Led 组件解耦通信 */ #include #include "button.h" #include "stm32f10x.h" /* ======================================================== * 内部常量(对外完全不可见) * ======================================================== */ #define MAX_BUTTONS 4 #define DEBOUNCE_MS 20 /* 去抖动时间 */ #define CLICK_TIMEOUT_MS 300 /* 单击超时(判断单/双击) */ /* ======================================================== * 内部 FSM 状态(对外完全不可见) * ======================================================== */ typedef enum { BTN_STATE_IDLE, /* 空闲 */ BTN_STATE_DEBOUNCE_PRESS, /* 去抖动-按下中 */ BTN_STATE_PRESSED, /* 已稳定按下 */ BTN_STATE_DEBOUNCE_RELEASE, /* 去抖动-释放中 */ BTN_STATE_WAIT_CLICK /* 等待第二次单击 */ } BtnFsmState; /* ======================================================== * 结构体定义(仅在此 .c 中可见) * ======================================================== */ struct Button_S { GPIO_TypeDef* port; uint16_t pin; uint8_t active_low; uint8_t pressed; /* 当前物理电平状态 */ BtnFsmState fsm_state; /* 内部 FSM */ uint32_t last_tick_ms; /* 上次时间戳 */ uint8_t click_count; /* 单击次数 */ ButtonCallback callback; void* context; uint8_t initialized; }; /* ======================================================== * 静态内存池 * ======================================================== */ static Button_S g_pool[MAX_BUTTONS]; static uint8_t g_used[MAX_BUTTONS]; /* ======================================================== * 内部辅助:GPIO 端口转换 * ======================================================== */ static GPIO_TypeDef* _port_from_id(uint8_t id) { switch (id) { case 0: return GPIOA; case 1: return GPIOB; case 2: return GPIOC; case 3: return GPIOD; case 4: return GPIOE; default: return NULL; } } static uint32_t _rcc_bit_from_port(GPIO_TypeDef* port) { if (port == GPIOA) return RCC_APB2Periph_GPIOA; else if (port == GPIOB) return RCC_APB2Periph_GPIOB; else if (port == GPIOC) return RCC_APB2Periph_GPIOC; else if (port == GPIOD) return RCC_APB2Periph_GPIOD; else if (port == GPIOE) return RCC_APB2Periph_GPIOE; return 0; } /* ======================================================== * 公开 API 实现 * ======================================================== */ Button_S* Button_Create(const ButtonConfig* cfg) { if (cfg == NULL) return NULL; GPIO_TypeDef* port = _port_from_id(cfg->gpio_port); if (port == NULL) return NULL; for (uint8_t i = 0; i < MAX_BUTTONS; i++) { if (g_used[i] == 0) { g_used[i] = 1; Button_S* inst = &g_pool[i]; inst->port = port; inst->pin = (uint16_t)(1u << cfg->gpio_pin); inst->active_low = cfg->active_low; inst->pressed = 0; inst->fsm_state = BTN_STATE_IDLE; inst->last_tick_ms = 0; inst->click_count = 0; inst->callback = cfg->callback; inst->context = cfg->context; inst->initialized = 0; /* 使能 GPIO 时钟 */ uint32_t rcc_bit = _rcc_bit_from_port(port); if (rcc_bit) RCC_APB2PeriphClockCmd(rcc_bit, ENABLE); /* 配置为输入(上拉) */ GPIO_InitTypeDef gpio_init; gpio_init.GPIO_Pin = inst->pin; gpio_init.GPIO_Mode = GPIO_Mode_IPU; gpio_init.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(port, &gpio_init); inst->initialized = 1; return inst; } } return NULL; } void Button_Poll(Button_S* me) { if (me == NULL || !me->initialized) return; /* 读取当前物理电平 */ uint8_t raw = (uint8_t)(GPIO_ReadInputDataBit(me->port, me->pin)); uint8_t active = me->active_low ? (raw == Bit_RESET) : (raw == Bit_SET); /* 简单 tick 计数器——实际工程中需用 SysTick 或 TIM */ /* 这里作为演示,不做精确时间测量 */ switch (me->fsm_state) { case BTN_STATE_IDLE: if (active) { me->fsm_state = BTN_STATE_DEBOUNCE_PRESS; me->last_tick_ms = 1; /* 占位,记录按下时刻 */ } break; case BTN_STATE_DEBOUNCE_PRESS: if (!active) { /* 抖动,回到空闲 */ me->fsm_state = BTN_STATE_IDLE; } else { /* 稳定按下 */ me->pressed = 1; me->fsm_state = BTN_STATE_PRESSED; if (me->callback) me->callback(BTN_EVT_PRESSED, me->context); } break; case BTN_STATE_PRESSED: if (!active) { me->pressed = 0; me->fsm_state = BTN_STATE_DEBOUNCE_RELEASE; me->last_tick_ms = 1; if (me->callback) me->callback(BTN_EVT_RELEASED, me->context); } break; case BTN_STATE_DEBOUNCE_RELEASE: if (active) { /* 抖动 */ me->fsm_state = BTN_STATE_PRESSED; me->pressed = 1; } else { /* 释放稳定 */ me->click_count++; me->fsm_state = BTN_STATE_WAIT_CLICK; } break; case BTN_STATE_WAIT_CLICK: /* 在 CLICK_TIMEOUT_MS 内等待第二次按下 */ if (active) { /* 第二次按下,判定为双击 */ me->click_count = 0; me->fsm_state = BTN_STATE_DEBOUNCE_PRESS; if (me->callback) me->callback(BTN_EVT_DBL_CLICK, me->context); } else { /* 超时无第二次按下,判定为单击 */ me->click_count = 0; me->fsm_state = BTN_STATE_IDLE; if (me->callback) me->callback(BTN_EVT_CLICK, me->context); } break; } } uint8_t Button_IsPressed(const Button_S* me) { if (me == NULL) return 0; return me->pressed; } void Button_Destroy(Button_S* me) { if (me == NULL) return; for (uint8_t i = 0; i < MAX_BUTTONS; i++) { if (&g_pool[i] == me) { g_used[i] = 0; return; } } }