Привет всем! Имеется Raspberry Pi 4B. Мне нужно с него одновременно управлять 20 адресными светодиодными лентами WS2812b. Из-за большого количества лент не получается использовать готовые библиотеки, т.к. они используют аппаратную PWM (широтноимпульсную модуляцию), и мне нужно переключать пины GPIO вручную, быстро и точно. Опытным путем я установил, что если делать 76 раз asm("nop"), то получается точно выдержать интервал 400 нс. Проблема в том, что периодически этот интервал становится гораздо больше, по-видимому из-за прерываний процессора.
Для теста я генерирую кодом меандр с периодом около 800 нс.
Я изолировал четвертое ядро контроллера через cpuisol=3, чтобы ОС (использую Raspbian) не перекидывала другие процессы на 4 ядро, а своей программе напрямую указал привязаться к четвертому ядру, помимо этого, я установил ей максимальный приоритет. Почти получилось выдерживать ровные интервалы, но всё еще периодически происходят сбои, и лента сбивается:
После этого я написал модуль ядра linux, драйвер, который также работает с gpio, только на время передачи данных делает spin_lock_irqsave(), а затем, после передачи данных - spin_unlock_irqsave(), чтобы запретить ОС выполнять прерывания во время передачи данных через GPIO. К моему удивлению, такой код работает даже хуже, чем тот, который работал из userspace без драйверов:
В версии с драйвером сбои происходят каждые несколько периодов, тогда как из userspace сбои достаточно редки.
Вопрос - что я делаю не так? Почему у драйвера результат хуже? Я не до конца отключил прерывания? Или дело в способе работы с gpio - ведь в userspace я работаю через wiringPi.h, а в kernelspace - через linux/gpio.h - может в этом дело? Как мне добиться стабильной работы?