diff --git a/bits_button.c b/bits_button.c index 18c5ecb..cf5696f 100644 --- a/bits_button.c +++ b/bits_button.c @@ -447,15 +447,6 @@ int32_t bits_button_init(const bits_btn_config_t *config) return BITS_BTN_ERR_TOO_MANY_BUTTONS; } - memset(button, 0, sizeof(bits_button_t)); - - button->btns = config->btns; - button->btns_cnt = config->btns_cnt; - button->btns_combo = config->btns_combo; - button->btns_combo_cnt = config->btns_combo_cnt; - button->_read_button_level = config->read_button_level_func; - button->bits_btn_result_cb = config->bits_btn_result_cb; - if (config->btns_combo_cnt > BITS_BTN_MAX_COMBO_BUTTONS) { if (debug_printf) @@ -503,6 +494,17 @@ int32_t bits_button_init(const bits_btn_config_t *config) } } + // All validations passed, now modify global state + memset(button, 0, sizeof(bits_button_t)); + + button->btns = config->btns; + button->btns_cnt = config->btns_cnt; + button->btns_combo = config->btns_combo; + button->btns_combo_cnt = config->btns_combo_cnt; + button->_read_button_level = config->read_button_level_func; + button->bits_btn_result_cb = config->bits_btn_result_cb; + + // Calculate combo_mask after btns_combo is assigned for(uint16_t i = 0; i < config->btns_combo_cnt; i++) { button_obj_combo_t *combo = &button->btns_combo[i]; @@ -578,6 +580,9 @@ void bits_button_reset_states(void) { bits_button_t *button = &bits_btn_entity; + if (button->_read_button_level == NULL || button->btns == NULL) + return; + if (debug_printf) debug_printf("Resetting all button states\n"); @@ -910,6 +915,10 @@ static void dispatch_unsuppressed_buttons(bits_button_t *button, button_mask_typ void bits_button_ticks(void) { bits_button_t *button = &bits_btn_entity; + + if (button->_read_button_level == NULL || button->btns == NULL) + return; + uint32_t current_time = get_button_tick(); button->btn_tick++; diff --git a/docs/buffer_modes.md b/docs/buffer_modes.md index 9ef8c4b..5a6b4d8 100644 --- a/docs/buffer_modes.md +++ b/docs/buffer_modes.md @@ -6,7 +6,7 @@ BitsButton库支持三种不同的缓冲区模式,以满足不同应用场景 ### 1. C11原子缓冲区模式(默认) -这是默认模式,适用于多线程环境,提供高性能和线程安全的特性。 +这是默认模式,使用C11原子操作提供高性能的无锁缓冲区。 **启用方式:** ```c @@ -17,8 +17,8 @@ gcc -c -std=c11 bits_button.c **特性:** - 使用C11原子操作实现SPSC(单生产者单消费者)环形缓冲区 -- 线程安全,无锁设计 -- 高性能,适用于多线程环境 +- 无锁设计,适用于单生产者单消费者场景 +- 缓冲区满时采用覆写策略(丢弃最旧事件),通过 `get_bits_btn_buffer_overwrite_count()` 可查询覆写次数 - 需要C11编译器支持 ### 2. 禁用缓冲区模式 @@ -135,7 +135,7 @@ void setup_custom_buffer(void) { |------|-------------|----------------|----------------| | 内存占用 | 中等 | 最小 | 可控 | | 性能 | 高 | 高 | 取决于实现 | -| 线程安全 | 是 | N/A | 取决于实现 | +| 线程安全 | SPSC(见注意事项) | N/A | 取决于实现 | | 轮询支持 | 是 | 否 | 是 | | 回调支持 | 是 | 是 | 是 | | 编译器要求 | C11 | C89+ | C89+ | @@ -146,4 +146,14 @@ void setup_custom_buffer(void) { 1. 禁用缓冲区模式下,`bits_button_get_key_result()` 函数将不可用 2. 用户自定义模式需要谨慎实现线程安全性 3. C11模式提供最佳的性能和安全性平衡 -4. 在资源受限环境中,禁用缓冲区模式可能是最佳选择 \ No newline at end of file +4. 在资源受限环境中,禁用缓冲区模式可能是最佳选择 +5. **C11 缓冲区的线程安全约束**:默认 C11 缓冲区基于 SPSC(单生产者单消费者)模型, + 要求 `bits_button_ticks()`(生产者)和 `bits_button_get_key_result()` / `bits_button_peek_key_result()`(消费者) + 不会并发访问同一个缓冲区槽位。在以下场景中请注意: + - **裸机主循环**:两者在同一循环中顺序调用,天然安全 + - **中断 + 主循环**:`bits_button_ticks()` 在定时器中断中调用时, + 若缓冲区满触发覆写,可能与主循环中的读取产生竞态。 + 建议确保缓冲区容量足够(通过 `BITS_BTN_BUFFER_SIZE` 宏调整),避免满溢 + - **RTOS 多线程**:若生产者和消费者在不同线程,建议使用 `BITS_BTN_USE_USER_BUFFER` + 模式,提供带互斥保护的自定义缓冲区实现 + - 无论哪种场景,**回调函数**(`bits_btn_result_cb`)始终会被同步调用,不经过缓冲区,不受此约束影响