Кратко
На K:04 с донглом Qube ввод периодически замирает целиком примерно на 1.3 секунды. Клавиша, которая была нажата в момент замирания, остаётся зажатой в HID-репорте, и хост уводит её в автоповтор — снаружи это выглядит как «залипла клавиша». Через ~1.3 с приходит release, и всё продолжается как ни в чём не бывало.
Проверено на стоковой прошивке 0.1.7 (тег v0.1.7), на чистом хосте, без стороннего софта.
Окружение
- K:04 + донгл Qube, беспроводной режим, две половины
- Прошивка 0.1.7 (стоковая, с сайта),
vendor_id = 0xE126, product_id = 0x0071
- Хост: Linux, ядро 7.1.8, донгл воткнут напрямую в порт материнской платы
Симптом
Мерил чтением /dev/input/event* напрямую — сырые evdev-события, минуя раскладку, IME и любые прослойки.
Эпизод целиком:
14:30:24.346 код 36 (J) нажатие
14:30:24.602 код 36 автоповтор ядра
14:30:24.636 код 36 автоповтор
... всего 32 повтора, ровно по 34 мс
14:30:25.660 код 36 автоповтор
14:30:25.682 код 36 отпускание ← через 1336 мс после нажатия
Ключевой момент: между нажатием и отпусканием прошло 1336 мс, и за всё это время с клавиатуры не пришло ни одного другого события. Я печатаю с интервалом 90–130 мс между клавишами, то есть за 1.3 с должно было прилететь около десяти событий. Их нет. Замирает не одна клавиша, а весь поток целиком.
Второй эпизод из того же лога, три минуты спустя:
14:33:24.225 код 22 нажатие Δ113 мс
14:33:24.330 код 22 отпускание Δ105 мс
14:33:24.435 код 38 (L) нажатие Δ105 мс ← ритм печати
14:33:24.690 код 38 автоповтор ×33, по 34 мс
14:33:25.5.. код 38 отпускание — 1.09 с
Клавиша ушла в автоповтор посреди слова, в темпе 105 мс между предыдущими нажатиями. Букву «l» на секунду никто не зажимает специально.
Для сравнения — нормальный участок того же лога:
14:30:23.303 код 57 нажатие Δ 83 мс
14:30:23.305 код 33 нажатие Δ 2 мс
14:30:23.363 код 33 отпускание Δ 58 мс
14:30:23.468 код 57 отпускание Δ105 мс
14:30:23.670 код 14 нажатие Δ202 мс
14:30:23.745 код 14 отпускание Δ 75 мс
Статистика
На стоковой 0.1.7: окно 19.9 минуты активной печати, 1654 события, два таких эпизода — 1.06 с и 1.09 с.
Оговорка про метод: так фиксируются только те замирания, при которых в момент заморозки была зажата клавиша, дающая автоповтор. Замирания в паузе между нажатиями этим способом не видны вообще, так что два эпизода за 20 минут — нижняя граница, а не оценка частоты.
По более ранним замерам другим методом (по паузе с последующим залпом накопленных событий, 58+ эпизодов за несколько часов печати): длительность 0.7–4.3 с, медиана около 1350 мс, частота порядка 70–100 эпизодов за час активной печати.
Ещё наблюдение: замирания бывают одновременными для обеих половин. Из 28 эпизодов в 12 после разморозки события приходили сразу с обеих половин одним залпом. То есть замирает что-то общее, а не связь с одной конкретной половиной.
Что исключено измерением
Всё перечисленное проверено на практике, а не рассуждением.
- Версия прошивки. Проверены четыре разные сборки плюс стоковая 0.1.7. Медиана замирания во всех 1336–1378 мс, различия в частоте в пределах статистического шума.
- Стороннее ПО на хосте. Демон, который слал донглу raw-HID пакеты (часы, раскладка), был полностью остановлен на всё время замера. Разницы нет.
- Соседняя 2.4 ГГц периферия. Приёмник Logitech Unifying физически вынимался из порта. Замирания остались.
- USB-хаб и питание по USB. Донгл переехал из хаба напрямую в порт материнской платы. Замирания остались, оба эпизода выше сняты уже на прямом порту.
- Хост. Отдельный детектор дрейфа планировщика замерял отклонение 10-миллисекундного сна каждую минуту:
worst = 0 ms в каждой минуте, включая ту, в которой было замирание ввода на 3.5 с.
- USB-уровень. В момент замирания в
dmesg пусто: ни ошибок xhci, ни таймаутов, ни -71/-110, ни реконнектов. С точки зрения хоста устройство просто молчит, а потом снова начинает говорить.
Подозрения
Дальше — уже гипотезы, а не измерения. Проверить их у меня пока не получилось, поэтому выкладываю как есть.
1. Приоритеты прерываний в макро-пути.
rmk-macro/src/codegen/chip/bind_interrupt.rs:243-246 отдаёт MPSL её штатные прерывания:
EGU0_SWI0 => ::nrf_sdc::mpsl::LowPrioInterruptHandler;
RADIO => ::nrf_sdc::mpsl::HighPrioInterruptHandler;
TIMER0 => ::nrf_sdc::mpsl::HighPrioInterruptHandler;
RTC0 => ::nrf_sdc::mpsl::HighPrioInterruptHandler;
При этом rmk-macro/src/codegen/chip/chip_init.rs:146-152 для nRF52 генерирует конфиг, в котором приоритеты не выставляются вообще:
let mut config = ::embassy_nrf::config::Config::default();
#dcdc_config
let p = ::embassy_nrf::init(config);
То есть gpiote_interrupt_priority и time_interrupt_priority остаются на значении по умолчанию — P0, на том же уровне, который MPSL держит за своими RADIO/TIMER0/RTC0.
Показательно, что в самом репозитории требование известно: examples/use_rust/nrf52840/src/main.rs:41-43 явно выставляет
config.gpiote_interrupt_priority = ::embassy_nrf::interrupt::Priority::P3;
config.time_interrupt_priority = ::embassy_nrf::interrupt::Priority::P3;
::embassy_nrf::interrupt::USBD.set_priority(::embassy_nrf::interrupt::Priority::P2);
Но для всего, что собирается через #[rmk_central] / #[rmk_peripheral], эта настройка теряется.
Задержанные радио-прерывания объяснили бы наблюдаемое: и замирание всего потока сразу, и то, что обе половины замирают синхронно, и то, что длительность стабильно держится около 1.3 с независимо от версии прошивки.
2. Дисплей на Qube. На донгле SPIM3 регистрируется через add_interrupt! в keyboards/k04/src/qube.rs:20-22 и по той же причине оказывается на P0. Это единственная периферия на донгле, которая активно шевелит SPI на 8 МГц во время печати. Собрал себе безэкранный вариант донгла, чтобы это проверить, — как будут результаты, отпишусь здесь же.
3. Источник тактирования MPSL. Если MPSL работает от RC-осциллятора (±500 ppm), а не от кварца 32.768 кГц, окна радио плавают сильнее, чем могли бы. Не проверял, есть ли на платах кварц.
Кратко
На K:04 с донглом Qube ввод периодически замирает целиком примерно на 1.3 секунды. Клавиша, которая была нажата в момент замирания, остаётся зажатой в HID-репорте, и хост уводит её в автоповтор — снаружи это выглядит как «залипла клавиша». Через ~1.3 с приходит release, и всё продолжается как ни в чём не бывало.
Проверено на стоковой прошивке 0.1.7 (тег
v0.1.7), на чистом хосте, без стороннего софта.Окружение
vendor_id = 0xE126,product_id = 0x0071Симптом
Мерил чтением
/dev/input/event*напрямую — сырые evdev-события, минуя раскладку, IME и любые прослойки.Эпизод целиком:
Ключевой момент: между нажатием и отпусканием прошло 1336 мс, и за всё это время с клавиатуры не пришло ни одного другого события. Я печатаю с интервалом 90–130 мс между клавишами, то есть за 1.3 с должно было прилететь около десяти событий. Их нет. Замирает не одна клавиша, а весь поток целиком.
Второй эпизод из того же лога, три минуты спустя:
Клавиша ушла в автоповтор посреди слова, в темпе 105 мс между предыдущими нажатиями. Букву «l» на секунду никто не зажимает специально.
Для сравнения — нормальный участок того же лога:
Статистика
На стоковой 0.1.7: окно 19.9 минуты активной печати, 1654 события, два таких эпизода — 1.06 с и 1.09 с.
Оговорка про метод: так фиксируются только те замирания, при которых в момент заморозки была зажата клавиша, дающая автоповтор. Замирания в паузе между нажатиями этим способом не видны вообще, так что два эпизода за 20 минут — нижняя граница, а не оценка частоты.
По более ранним замерам другим методом (по паузе с последующим залпом накопленных событий, 58+ эпизодов за несколько часов печати): длительность 0.7–4.3 с, медиана около 1350 мс, частота порядка 70–100 эпизодов за час активной печати.
Ещё наблюдение: замирания бывают одновременными для обеих половин. Из 28 эпизодов в 12 после разморозки события приходили сразу с обеих половин одним залпом. То есть замирает что-то общее, а не связь с одной конкретной половиной.
Что исключено измерением
Всё перечисленное проверено на практике, а не рассуждением.
worst = 0 msв каждой минуте, включая ту, в которой было замирание ввода на 3.5 с.dmesgпусто: ни ошибок xhci, ни таймаутов, ни-71/-110, ни реконнектов. С точки зрения хоста устройство просто молчит, а потом снова начинает говорить.Подозрения
Дальше — уже гипотезы, а не измерения. Проверить их у меня пока не получилось, поэтому выкладываю как есть.
1. Приоритеты прерываний в макро-пути.
rmk-macro/src/codegen/chip/bind_interrupt.rs:243-246отдаёт MPSL её штатные прерывания:При этом
rmk-macro/src/codegen/chip/chip_init.rs:146-152для nRF52 генерирует конфиг, в котором приоритеты не выставляются вообще:То есть
gpiote_interrupt_priorityиtime_interrupt_priorityостаются на значении по умолчанию — P0, на том же уровне, который MPSL держит за своими RADIO/TIMER0/RTC0.Показательно, что в самом репозитории требование известно:
examples/use_rust/nrf52840/src/main.rs:41-43явно выставляетНо для всего, что собирается через
#[rmk_central]/#[rmk_peripheral], эта настройка теряется.Задержанные радио-прерывания объяснили бы наблюдаемое: и замирание всего потока сразу, и то, что обе половины замирают синхронно, и то, что длительность стабильно держится около 1.3 с независимо от версии прошивки.
2. Дисплей на Qube. На донгле
SPIM3регистрируется черезadd_interrupt!вkeyboards/k04/src/qube.rs:20-22и по той же причине оказывается на P0. Это единственная периферия на донгле, которая активно шевелит SPI на 8 МГц во время печати. Собрал себе безэкранный вариант донгла, чтобы это проверить, — как будут результаты, отпишусь здесь же.3. Источник тактирования MPSL. Если MPSL работает от RC-осциллятора (±500 ppm), а не от кварца 32.768 кГц, окна радио плавают сильнее, чем могли бы. Не проверял, есть ли на платах кварц.