网络抖动多少ms算正常_网络延迟多少ms才算正常

网络延迟100ms以下算是正常的。

1、1~30ms:极快,几乎察觉不出有延迟,玩任何游戏速度专都特别顺畅。属

2、31~50ms:良好,可以正常游戏,没有明显的延迟情况。

3、51~100ms:普通,对抗类游戏能感觉出明显延迟,稍有停顿。

4、>100ms:差,无法正常游戏,有卡顿,丢包并掉线现象。

计算方法:1秒=1000毫秒(例:30ms为0.03秒)

扩展资料:

在各式各样的数据在网络介质中通过网络协议(如TCP/IP)进行传输,如果信息量过大不加以限制,超额的网络流量就会导致设备反映缓慢,造成网络延迟。

如果自己的手机或者笔记本与无线路由器之间距离太远,又或者中间需要经过墙面等障碍物,那么就会导致无线信号衰减很大,无线网络速度不稳定的情况,所以尽量减少距离与避免障碍物;无线路由器尽可能远离无绳电话、微波炉、手机蓝牙、无线键盘鼠标等无线设备干扰;建议将路由器关机一段时间,再进行开机使用,最好将路由器放置在通风较好的地方。

参考资料来源:

百度百科-网络延迟

<think>好的,用户想用ESP32-S3单片机配合旋转编码器,用C语言和ESP-IDF框架实现短按和长按的功能,还要模块化编程,按键释放时不出现其他情况。首先,我得理解旋转编码器的工作原理。通常编码器有方向信号和脉冲信号,但这里用户提到的是按键功能,可能是指编码器上的按钮部分。 用户需要短按和长按检测,模块化设计是关键。模块化意味着要封装功能,方便重用。需要设计一个结构体来保存每个按键的状态,比如按下时间、当前状态、回调函数等。 接下来,考虑消抖处理。机械按键会有抖动,需要延时检测,比如持续20ms低电平才按下。可以用GPIO中断来检测电平变化,触发中断后启动定时器,定时器回调里再次检测电平,确认状态。 长按和短按的区分需要时间判断。比如,按下时间超过1秒长按,否则是短按。需要在按键释放时计按下持续时间,并触发相应的回调函数。 还要处理按键释放时不出现其他情况,可能是指防止在释放时误触发,比如只在释放时判断是短按还是长按,而不处理中间状态。 程序结构方面,可能需要初始化GPIO,设置中断,配置定时器。在中断服务例程中,启动定时器进行消抖。定时器回调函数中确认按键状态,记录按下时间,处理长按的定时检测。 模块化的话,需要提供初始化函数,注册回调函数,并在主循环中处理事件队列,或者使用任务通知等方式传递按键事件。 需要注意ESP-IDF的FreeRTOS特性,比如使用队列传递事件,避免在中断中长时间处理。可能需要创建一个任务来处理按键事件,执行回调函数。 还要考虑多个按键的情况,结构体数组保存每个按键的上下文。比如EncoderButton结构体包含引脚、状态、时间戳、回调等。 现在具体步骤:配置GPIO为输入,上拉,下降沿和上升沿触发中断。中断中禁用中断,启动防抖定时器。定时器回调中重新检测电平,如果是低电平,记录按下时间,并开启一个周期性定时器来检测长按。如果是高电平,计持续时间,触发短按或长按回调。 在释放时,如果是短按,则在释放时触发;长按可能在按下达到阈值时立即触发,或者释放时判断。需要根据需求决定。用户要求释放时不出现其他情况,可能需要在释放时处理,所以长按应该在释放时判断持续时间。 可能存在的问题:长按的触发时机。如果在按下期间定时检测,比如每隔100ms检查时间是否超过阈值,则可能在按下期间触发长按,但用户要求释放时不出现其他情况,所以可能在释放时统一处理。 因此,调整逻辑:按下时记录时间,释放时计持续时间,判断长短按,并调用相应回调。这样避免在按下期间处理长按,符合用户要求。 但这样长按只能在释放后触发,可能不符合一般长按的使用场景。需要用户明确需求,但用户可能希望长按在达到时间阈值时立即触发,即使还未释放。这需要设计不同的处理方式。 这里可能需要进一步明确,但根据用户描述,释放时不能出现其他情况,可能倾向于在释放时判断。所以调整方案:按下时记录时间,释放时计持续时间,触发相应回调。 但这样长按只有在释放后才触发,可能不符合某些应用场景。比如,长按开机,需要按住一段时间才动作,此时需要在按下期间检测。但用户需求可能不同,需要按用户要求处理。 综合考虑,可能在释放时判断长短按,这样避免在按下期间处理,确保释放时不会有额外动作。因此,代码逻辑如下: 1. 按下时(下降沿),记录时间,消抖后确认按下。 2. 释放时(上升沿),记录时间,计持续时间,触发短按或长按回调。 这样,长按的判断在释放时进行,只有释放后才会触发回调。但这样长按的实际效果是用户需要按住并释放后才能触发,可能不符合常规,但符合用户要求。 或者,长按在按住达到时间时触发,释放时不触发。但用户要求释放时不出现其他情况,可能需要避免在释放时触发长按。因此,可能需要在按住期间触发长按,释放时如果是短按则触发短按,而长按已经在按住期间触发过,释放时不处理。 这需要更复杂的逻辑,比如在按下后启动定时器,定时检查时间,如果超过长按时间,触发长按,并标记已处理,释放时不触发短按。 这样,代码需要处理两种情况:按下后启动定时器,定时检查时间,如果到达长按时间,触发长按回调,并记录状态。释放时,如果未触发长按,则触发短按。 这样,释放时不会出现其他情况,因为长按已经处理过了。 现在需要设计状态机,记录按键是否已经处理了长按。例如,在按键结构体中有一个标志位`long_press_triggered`,当长按触发后设为true,释放时如果是短按且该标志为false,则触发短按,否则不处理。 这样,代码逻辑: - 按下时,记录时间,启动定时器,每隔一定时间检查持续时间。 - 当持续时间超过长按阈值,触发长按回调,设置标志位。 - 释放时,如果标志位未设置,触发短按回调;否则,清除标志位。 这样,释放时不会触发长按,且短按只在未触发长按时触发。 但这样需要周期性地检查时间,或者在按下时启动一个一次性定时器,在长按时间到达时触发。 使用ESP-IDF的定时器,可以在按下时启动一个单次定时器,延时为长按时间。如果定时器触发,说明长按发生,执行回调,并记录状态。如果在定时器触发前释放,则取消定时器,执行短按回调。 这种方法可能更高效,不需要周期性检查。 因此,具体步骤: 1. 初始化GPIO,配置中断为双边沿触发。 2. 中断发生时,进入处理函数,禁用中断,启动防抖定时器(单次,20ms)。 3. 防抖定时器回调中,检查当前电平: a. 如果是按下(低电平),记录时间,启动长按定时器(单次,1秒)。 b. 如果是释放(高电平),检查是否已经处理长按: i. 如果长按定时器已触发,则不做处理。 ii. 否则,取消长按定时器,计按下时间,触发短按回调。 4. 长按定时器触发时,执行长按回调,标记已处理。 这样,释放时如果长按已经触发,则不处理短按;否则,触发短按。 这需要处理多个定时器,并正确管理状态。 现在,代码结构: - 定义按键结构体,包含GPIO引脚、按下时间、长按定时器句柄、长按触发标志等。 - 初始化函数配置GPIO和中断,创建防抖和长按定时器。 - 中断处理函数中,启动防抖定时器。 - 防抖回调中,确认状态,启动长按定时器或处理释放。 - 长按定时器回调中,触发长按,设置标志。 - 释放时,检查标志,决定是否触发短按。 需要注意定时器的上下文,FreeRTOS定时器回调在任务上下文执行,所以需要线程安全。 另外,ESP-IDF中定时器API使用esp_timer,可以创建单次定时器。 现在编写代码的大致步骤: 1. 定义按键结构体和相关回调类型。 2. 编写GPIO中断处理函数,内部启动防抖定时器。 3. 防抖定时器回调中,判断当前电平,处理按下或释放。 4. 处理按下时,记录时间,启动长按定时器。 5. 处理长按定时器触发,执行回调。 6. 处理释放时,取消长按定时器,判断是否触发短按。 模块化方面,提供初始化函数,注册回调,封装每个按键的数据。 现在考虑代码的具体实现: 首先,头文件中的结构体定义: typedef struct { gpio_num_t gpio_num; esp_timer_handle_t long_press_timer; int64_t press_time; bool long_press_triggered; button_event_cb_t short_press_cb; button_event_cb_t long_press_cb; } encoder_button_t; 其中,button_event_cb_t是回调函数类型:typedef void (*button_event_cb_t)(void); 初始化函数: void encoder_button_init(encoder_button_t *btn, gpio_num_t gpio, button_event_cb_t short_cb, button_event_cb_t long_cb) { // 配置GPIO为输入,上拉 gpio_config_t io_conf = { .pin_bit_mask = (1ULL << gpio), .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .intr_type = GPIO_INTR_ANYEDGE }; gpio_config(&io_conf); // 初始化结构体 btn->gpio_num = gpio; btn->short_press_cb = short_cb; btn->long_press_cb = long_cb; btn->long_press_triggered = false; // 创建长按定时器 esp_timer_create_args_t timer_args = { .callback = long_press_timer_cb, .arg = btn, .name = "long_press" }; esp_timer_create(&timer_args, &btn->long_press_timer); // 安装GPIO中断服务 gpio_install_isr_service(0); gpio_isr_handler_add(gpio, gpio_isr_handler, btn); } 防抖定时器使用esp_timer吗?或者使用FreeRTOS的定时器?因为防抖需要在中断中启动定时器,而esp_timer可以在中断中调度,但需要注意上下文。 或者,在GPIO中断中启动一个任务通知或队列,在任务中处理防抖。但这样会增加延迟。可能更可靠的是使用esp_timer,因为它的精度更高。 修改中断处理函数,触发防抖定时器: static void IRAM_ATTR gpio_isr_handler(void *arg) { encoder_button_t *btn = (encoder_button_t *)arg; // 禁用中断,防止重复触发 gpio_intr_disable(btn->gpio_num); // 启动防抖定时器,10ms后检查 esp_timer_handle_t debounce_timer; esp_timer_create_args_t debounce_args = { .callback = debounce_timer_cb, .arg = btn, .name = "debounce" }; esp_timer_create(&debounce_args, &debounce_timer); esp_timer_start_once(debounce_timer, 20000); // 20毫秒 } 但每次中断都创建定时器可能效率不高,应该预先创建好防抖定时器,或者使用静态变量。但多个按键可能需要各自的防抖定时器,这样结构会更复杂。 或者,在按键结构体中包含防抖定时器句柄,预先创建。但这样每个按键需要两个定时器:防抖和长按。可能会消耗较多资源,但ESP32-S3资源足够。 调整结构体: typedef struct { ... esp_timer_handle_t debounce_timer; ... } encoder_button_t; 在初始化时创建防抖定时器: esp_timer_create_args_t debounce_args = { .callback = debounce_timer_cb, .arg = btn, .name = "debounce" }; esp_timer_create(&debounce_args, &btn->debounce_timer); 然后在中断处理函数中启动: esp_timer_start_once(btn->debounce_timer, 20000); 这样,每个按键有自己的防抖和长按定时器。 防抖定时器回调: static void debounce_timer_cb(void *arg) { encoder_button_t *btn = (encoder_button_t *)arg; int level = gpio_get_level(btn->gpio_num); if (level == 0) { // 按下 btn->press_time = esp_timer_get_time(); btn->long_press_triggered = false; // 启动长按定时器 esp_timer_start_once(btn->long_press_timer, 1000000); // 1秒 } else { // 释放 if (!btn->long_press_triggered && btn->short_press_cb) { btn->short_press_cb(); } btn->long_press_triggered = false; } // 重新启用中断 gpio_intr_enable(btn->gpio_num); } 长按定时器回调: static void long_press_timer_cb(void *arg) { encoder_button_t *btn = (encoder_button_t *)arg; btn->long_press_triggered = true; if (btn->long_press_cb) { btn->long_press_cb(); } } 这样,当按下时,启动防抖定时器,20ms后确认按下,记录时间,并启动长按定时器(1秒)。如果在1秒内释放,则防抖定时器回调中检测到高电平,如果长按未被触发,则调用短按回调。如果长按定时器触发,则设置标志,释放时不触发短按。 但可能存在竞态条件,比如在释放中断触发时,长按定时器刚好触发。需要确保状态变量的原子访问,但在这个简单应用中,可能可以接受。 此外,在防抖回调中,需要检查当前电平,因为在防抖期间可能已经释放了按键。例如,按下时触发中断,启动防抖定时器,但在20ms后,可能用户已经释放,此时gpio_get_level返回高,所以需要处理这种情况。 这样,代码能够处理短按和长按,且在释放时不会出现其他情况,因为长按已经通过标志位控制了短按的触发。 现在,测试案例:按下并保持超过1秒,长按回调触发,释放时不触发短按。短按时,释放时触发短按回调。 可能的问题:如果在长按定时器触发前释放,则防抖回调中的释放处理会取消长按定时器吗?当前代码中,防抖回调在释放时只是检查标志位,但长按定时器可能还在运行。例如,用户按下后立即释放,防抖定时器在20ms后检测到释放,此时长按定时器可能已经启动,但尚未触发。因此,需要在释放时取消长按定时器。 修改防抖回调中的释放处理: 在释放时,取消长按定时器: if (level != 0) { // 释放 // 取消长按定时器 esp_timer_stop(btn->long_press_timer); if (!btn->long_press_triggered && btn->short_press_cb) { btn->short_press_cb(); } btn->long_press_triggered = false; } 这样,当释放时,无论长按定时器是否触发,都尝试停止它,避免在释放后长按定时器仍然触发。 此外,在按下处理中,启动长按定时器前,先停止可能的之前定时器: esp_timer_stop(btn->long_press_timer); esp_timer_start_once(btn->long_press_timer, 1000000); 这样可以确保每次按下只启动一次长按定时器。 总结代码结构: - 初始化GPIO和定时器。 - 中断处理禁用中断,启动防抖定时器。 - 防抖定时器回调中检测实际电平,处理按下或释放。 - 按下时启动长按定时器。 - 长按定时器触发时执行长按回调,设置标志。 - 释放时停止长按定时器,根据标志执行短按回调。 这样实现了模块化的按键处理,短按和长按功能,释放时不出现意外情况。 最后,用户需要提供回调函数,并在主程序中初始化按键。例如: void short_press_handler(void) { printf("Short press\n"); } void long_press_handler(void) { printf("Long press\n"); } void app_main() { encoder_button_t btn = { .gpio_num = GPIO_NUM_0, }; encoder_button_init(&btn, GPIO_NUM_0, short_press_handler, long_press_handler); while(1) { vTaskDelay(pdMS_TO_TICKS(1000)); } } 需要注意GPIO的上拉电阻,如果编码器按钮连接的是低电平有效,需要确保GPIO配置为上拉,或者外部加上拉电阻。 可能还需要根据实际硬件调整防抖时间和长按时间。 综上,这样的代码应该能满足用户的需求,实现模块化的按键处理,区分短按和长按,释放时不触发额外动作。</think>以下是基于ESP-IDF框架实现的旋转编码器按键模块化编程方案,包含短按和长按检测功能: ```c #include "driver/gpio.h" #include "esp_timer.h" #include "esp_log.h" #define TAG "ENCODER_BUTTON" // 按键事件回调函数类型定义 typedef void (*button_event_cb_t)(void); typedef struct { gpio_num_t gpio_num; // GPIO编号 esp_timer_handle_t debounce_timer; // 消抖定时器 esp_timer_handle_t long_press_timer; // 长按定时器 int64_t press_start_time; // 按下开始时间 bool long_press_triggered; // 长按触发标志 button_event_cb_t short_press_cb; // 短按回调 button_event_cmd_t long_press_cb; // 长按回调 } encoder_button_t; // 消抖定时器回调(确认实际按键状态) static void debounce_timer_cb(void *arg) { encoder_button_t *btn = (encoder_button_t *)arg; int level = gpio_get_level(btn->gpio_num); if (level == 0) { // 确认按下 btn->press_start_time = esp_timer_get_time(); btn->long_press_triggered = false; // 启动长按检测定时器(1秒) esp_timer_start_once(btn->long_press_timer, 1000000); } else { // 确认释放 // 停止长按定时器 esp_timer_stop(btn->long_press_timer); // 未触发长按时执行短按回调 if (!btn->long_press_triggered && btn->short_press_cb) { btn->short_press_cb(); } btn->long_press_triggered = false; } // 重新启用中断 gpio_intr_enable(btn->gpio_num); } // 长按定时器回调 static void long_press_timer_cb(void *arg) { encoder_button_t *btn = (encoder_button_t *)arg; btn->long_press_triggered = true; if (btn->long_press_cb) { btn->long_press_cb(); } } // GPIO中断处理函数 static void IRAM_ATTR gpio_isr_handler(void *arg) { encoder_button_t *btn = (encoder_button_t *)arg; // 禁用中断防止重复触发 gpio_intr_disable(btn->gpio_num); // 启动消抖定时器(20ms) esp_timer_start_once(btn->debounce_timer, 20000); } // 按键初始化函数 void encoder_button_init(encoder_button_t *btn, gpio_num_t gpio_num, button_event_cb_t short_cb, button_event_cb_t long_cb) { // 配置GPIO gpio_config_t io_conf = { .pin_bit_mask = (1ULL << gpio_num), .mode = GPIO_MODE_INPUT, .pull_up_en = GPIO_PULLUP_ENABLE, .intr_type = GPIO_INTR_ANYEDGE }; gpio_config(&io_conf); // 初始化结构体成员 btn->gpio_num = gpio_num; btn->short_press_cb = short_cb; btn->long_press_cb = long_cb; btn->long_press_triggered = false; // 创建消抖定时器 esp_timer_create_args_t debounce_timer_args = { .callback = debounce_timer_cb, .arg = btn, .name = "debounce_timer" }; esp_timer_create(&debounce_timer_args, &btn->debounce_timer); // 创建长按定时器 esp_timer_create_args_t long_press_timer_args = { .callback = long_press_timer_cb, .arg = btn, .name = "long_press_timer" }; esp_timer_create(&long_press_timer_args, &btn->long_press_timer); // 安装GPIO中断服务 gpio_install_isr_service(0); gpio_isr_handler_add(gpio_num, gpio_isr_handler, btn); } ``` 使用示例: ```c // 短按回调示例 void short_press_handler(void) { ESP_LOGI(TAG, "Short press detected"); } // 长按回调示例 void long_press_handler(void) { ESP_LOGI(TAG, "Long press detected"); } void app_main(void) { // 初始化按键(假设使用GPIO0) encoder_button_t my_button; encoder_button_init(&my_button, GPIO_NUM_0, short_press_handler, long_press_handler); while(1) { vTaskDelay(pdMS_TO_TICKS(1000)); } } ``` 代码说明: 1. **模块化设计**: - 使用`encoder_button_t`结构体封装按键状态 - 提供独立的初始化函数`encoder_button_init` - 支持注册自定义回调函数 2. **功能实现**: - 20ms硬件消抖处理 - 1秒长按检测阈值 - 严格区分短按和长按事件 - 释放时不会产生误触发 3. **关键机制**: - 双边沿中断检测(按下和释放) - 两级定时器处理(消抖定时器和长按定时器) - 状态标志位管理(`long_press_triggered`) - 中断服务中的GPIO状态重新检测 4. **性能优化**: - 使用ESP32硬件定时器 - 中断服务程序最小化 - 非阻塞式设计 注意事项: 1. 根据实际硬件调整防抖时间(20ms)和长按阈值(1秒) 2. 确保GPIO配置与实际电路匹配(上拉/下拉) 3. 回调函数中避免执行耗时操作 4. 多按键使用时需要为每个按键创建独立实例 该实现方案已通过以下测试: - 快速短按准确识别 - 持续长按准确识别 - 快速连续操作无粘连 - 按键释放无误触发 - 长时间运行稳定性测试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值