From 65c5b1daaa4f0ff4456c8414b0323d16c79cd405 Mon Sep 17 00:00:00 2001 From: xueben Date: Tue, 10 Mar 2026 00:16:29 +0800 Subject: [PATCH] =?UTF-8?q?FIX:=20=E5=A2=9E=E5=8A=A0=E6=9C=AA=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E8=B0=83=E7=94=A8=E9=98=B2=E6=8A=A4=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=88=9D=E5=A7=8B=E5=8C=96=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F=20Why:=201.=20bits=5Fbutton=5Fticks()=20?= =?UTF-8?q?=E5=92=8C=20bits=5Fbutton=5Freset=5Fstates()=20=E5=9C=A8?= =?UTF-8?q?=E6=9C=AA=E8=B0=83=E7=94=A8=20bits=5Fbutton=5Finit()=20?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=20=20=20=20=E4=BC=9A=E5=9B=A0=20=5Fread=5Fbu?= =?UTF-8?q?tton=5Flevel=20=E4=B8=BA=20NULL=20=E5=AF=BC=E8=87=B4=E5=B4=A9?= =?UTF-8?q?=E6=BA=83=EF=BC=8C=E7=8E=B0=E5=9C=A8=E5=85=A5=E5=8F=A3=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20NULL=20=E9=98=B2=E6=8A=A4=E6=8F=90=E5=89=8D?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=EF=BC=9B=202.=20bits=5Fbutton=5Finit()=20?= =?UTF-8?q?=E5=9C=A8=E6=A0=A1=E9=AA=8C=E5=AE=8C=E6=88=90=E5=89=8D=E5=B0=B1?= =?UTF-8?q?=20memset=20=E6=B8=85=E9=9B=B6=E5=85=A8=E5=B1=80=E7=8A=B6?= =?UTF-8?q?=E6=80=81=EF=BC=8C=E5=AF=BC=E8=87=B4=E5=90=8E=E7=BB=AD=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=A4=B1=E8=B4=A5=E6=97=B6=20=20=20=20=E4=B9=8B?= =?UTF-8?q?=E5=89=8D=E6=88=90=E5=8A=9F=E7=9A=84=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E8=A2=AB=E7=A0=B4=E5=9D=8F=E3=80=82=E7=8E=B0?= =?UTF-8?q?=E5=B0=86=E6=89=80=E6=9C=89=E5=8F=82=E6=95=B0=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E7=A7=BB=E8=87=B3=20memset=20=E4=B9=8B=E5=89=8D=EF=BC=8C?= =?UTF-8?q?=E7=A1=AE=E4=BF=9D=E6=A0=A1=E9=AA=8C=20=20=20=20=E5=85=A8?= =?UTF-8?q?=E9=83=A8=E9=80=9A=E8=BF=87=E5=90=8E=E6=89=8D=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E7=8A=B6=E6=80=81=EF=BC=9B=203.=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=20buffer=5Fmodes.md=20=E4=B8=AD=20C11=20=E7=BC=93?= =?UTF-8?q?=E5=86=B2=E5=8C=BA=E7=9A=84=20SPSC=20=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E7=BA=A6=E6=9D=9F=E8=AF=B4=E6=98=8E=EF=BC=8C?= =?UTF-8?q?=20=20=20=20=E6=98=8E=E7=A1=AE=E8=A3=B8=E6=9C=BA=E4=B8=BB?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF=E3=80=81=E4=B8=AD=E6=96=AD+=E4=B8=BB?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF=E3=80=81RTOS=20=E5=A4=9A=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E4=B8=89=E7=A7=8D=E5=9C=BA=E6=99=AF=E7=9A=84=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bits_button.c | 27 ++++++++++++++++++--------- docs/buffer_modes.md | 20 +++++++++++++++----- 2 files changed, 33 insertions(+), 14 deletions(-) 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`)始终会被同步调用,不经过缓冲区,不受此约束影响