Драйвер для STM32 для реализации протокола адресных светодиодов (WS2812, WS2811, SK6812, и т.д.), с рациональным использованием буферной памяти и DMA.  Ссылка на библиотеку на GitHub: Ролик на YouTube: На пин DIN первого светодиода (начало ленты) подаётся сигнал, формируемый STM32. Из-за разницы питающих напряжений, сигнал следует поднять до уровня 5 вольт с помощью специальной микросхемы-транслятора логики или с помощью настройки GPIO-пина в режиме Open Drain, подтянув его резистором. ВАЖНО! При использовании Open Drain нужно убедиться, что пин выдержит 5 вольт. Узнать это можно в даташите на свой МК. Пример:   ОГРАНИЧЕНИЯ: Из-за особенностей таймеров, минимально стабильная частота работы микроконтроллера — 32 МГц.  Сперва нужно настроить таймер в режиме ШИМ. Обратите внимание на отмеченные стрелками настройки.   Отправка значений в таймер происходит с использованием DMA, поэтому настроим и этот блок.   Ножка должна иметь наивысшую скорость из доступных. Если выбран режим Open Drain, то не забудьте переключиться.   Также проверьте, что генерация DMA_Init стоит выше, чем TIM_Init. Иначе таймер не узнает про DMA, сигнал генерироваться не будет.   Сгенерируем код, добавим файлы библиотеки в проект. Откроем .h-файл и посмотрим, что можно настроить. Теперь, для проверки, можно попробовать забилдить проект и посмотреть на доступные функции. Все методы возвращают enum-статусы.   Адресные светодиоды ленты используют для различной индикации, как бытовой, так и коммерческой. Ключевое отличие от обычных RGB-диодов в том, что их можно зажигать отдельно, каждый своим цветом.   Это поведение обусловлено тем, что у каждого диода стоит чип-драйвер. Снаружи, как в случае с WS2811, или внутри, как у WS2812 и остальных. Чип принимает сигнал, запоминает первые импульсы, а остальные передаёт далее по цепочке.   Свечение каждого субпикселя кодируется 8 битами. Т.е. для RGB (WS281X) 24 бита, для RGBW (SK6812) 32 бита. Код бита задаётся длиной импульса, то есть скважностью.   Существует и код RET — пауза, означающая конец передачи.   У всех контроллеров разные тайминги:  WS2811 (slow) WS2811 (fast, SET=1) WS2812(b) SK6812 Частота 400 КГц 800 КГц 800 КГц 800 КГц Период (Т) 2,5 мкс 1,25 мкс 1,25 мкс 1,25 мкс T0H 0,5 мкс (20%) 0,25 мкс (20%) 0,35 мкс (28%) 0,3 мкс (24%) T1H 1,2 мкс (48%) 0,6 мкс (48%) 0,7 мкс (56%) 0,6 мкс (48%) T0L 2,0 мкс 1,0 мкс 0,8 мкс 0,9 мкс T1L 1,3 мкс 0,65 мкс 0,6 мкс 0,6 мкс Допуск +/- 150 нс +/- 150 нс +/- 150 нс +/- 150 нс RET > 50 мкс (20Т) > 50 мкс (40Т) > 50 мкс (40Т) > 80 мкс (64Т) Большинство решений основаны на использовании пустых тактов. Это означает, что весь процессор тормозит на время отправки сигнала. Такой способ не только тратит уйму процессорного времени, но и рискует сломаться, в случае возникновения прерывания.   Посчитаем длину передачи сигнала на 1 диод: 1,25 мкс * 24 бит = 30 мкс.Для n диодов: T = 30 * n + 50 мкс. 30 диодов — уже 1 миллисекунда. Иными словами, протокол на задержках стоит использовать только для малого количества диодов, чтобы не мешать основной программе. Именно из-за этой проблемы я в своё время впервые обратился к STM32. В других вариантах используется шина SPI, которую настраивают на частоту 800 КГц. Я не проверял, но многие пишут про ощутимую потерю точности сигнала.   Что же делать?   В почти всех микроконтроллерах STM32 существует блок DMA (Direct Memory Access). Он позволяет передавать данные между периферией и памятью в разных направлениях без участия процессора.   В качестве исполнительной периферии используется таймер, настроенный в режиме ШИМ.  Любой способ передачи сигнала подразумевает буфер, в котором хранятся значения скважности сигнала.   В сети встречаются множество вариантов буфера сразу под все диоды. Чаще всего скважность 8-битная, поэтому такой будет весить N диодов * 24 байт. Уже под 100 диодов он займёт более 2 КБ ОЗУ.  А если записывать скважность с шириной 32 бита, как требуют некоторые серии МК, под 100 диодов буфер будет более 9 КБ.  Реализация моего метода была придумана не мной. В ней очень хитро используется память. Буфер здесь двойной. Первый имеет размер N диодов * 3 байта. В нём хранится цвет в представлении RGB. Второй буфер — для скважностей. Он фиксированный, занимает всего 48 байт, или 64 байта для RGBW. В него вмещаются всего 2 диода. Прерывания DMA позволяют заполнять одну часть буфера, пока отправляется вторая. Используя такой подход, можно растягивать цепочку диодов почти до бесконечности, покуда хватит памяти для первичного буфера или частоты обновления.  Дело в том, что адресные диоды воспринимают сигнал, опираясь на напряжение своего питания. Открыв даташит на WS2812b, мы увидим такие строки:    Min Max VIH 0,7 VDD — VIL — 0,3 VDD Это — границы восприятия сигнала. Иными словами, при питании от 5.1—5.2 Вольт, минимальный уровень сигнала — 3.57 Вольт. Так как STM32 выдаёт сигнал величиной 3.0—3.3 Вольта, его нужно увеличить.   Вариантов это сделать несколько:  Уменьшить напряжение питания ленты  Отрегулировать напряжение на БП Для небольшого отрезка запитать всю ленту через диод Отрезать первый светодиод, и запитать только его через диод Поднять потенциал GND микроконтроллера (подробнее) Воспользоваться преобразованием логики Так как предполагается использование в коммерческих проектах, где необходима не только надёжность, но и возможность беспроблемной замены отдельных компонентов пользователем, то самый безопасный вариант — последний. Способы преобразования логики были рассмотрены в данной статье. В ней сделаны выводы о том, что самый подходящий преобразователь — SN74LVC. Однако, при его отсутствии или для удешевления BOM, можно воспользоваться режимом Open Drain. DMA настраивается в кольцевом режиме передачи. Новые транзакции будут возникать до тех пор, пока не будут остановлены вручную в коде.   DMA генерирует прерывания каждую половину транзакции. Поэтому наш буфер размером в 2 диода. Пока идёт передача сигнала для первого диода, просчитывается и загружается сигнал для второго.   1-я половина 2-я половина Счётчик   LED [0] LED [1] 0 LED [2] LED [1] 1 LED [2] LED [3] 2 LED [4] LED [3] 3 LED [4] RET {1} 4 RET {2} RET {1} 5 RET {2} DMA_STOP 6 Состояние буфера. Bold — текущая передача   В первую очередь я столкнулся с согласованием логики. От USB компьютера всё работало, а от любого блока питания — нет. Решение пришло после пары тыков вольтметром и чтения даташита. Оказалось, что порты компьютера под просадкой выдавали порядка 4.6 Вольт, что есть 3,2 Вольта логической единицы. А все блоки питания стандартно выдавали в районе 5.2 Вольт, поэтому лента даже не зажигалась. Вторую проблему принесла библиотека HAL. Дело в том, что у базовых таймеров нет IDLE-состояния ног. Поэтому, после остановки таймера, ножка входила в Z-состояние, а подтяжка выкидывала сигнал вверх. На осцилограмме видно последовательность: сигнал, RET (пауза), Z-state, запуск таймера, сигнал, RET. Это приводило к тому, что первый диод в ленте считывал этот импульс и зажигался.   Даже с помощью остановки-запуска таймера невозможно было достичь нормальной работы. Либо из-за массивности HAL-функций, либо из-за особенностей работы периферии, возникала небольшая временная задержка, которой было достаточно для зажигания этого диода.   Решение было не таким очевидным, но нашлось довольно быстро. В функции HAL_TIM_PWM_Stop_DMA была обнаружена такая строчка:   Это и есть отключение GPIO-канала таймера. После её удаления, удалось достичь стабильной работы. Поэтому пришлось скопировать весь код этого метода к себе и немного отредактировать.   Третья проблема — фундаментальная. Заключается в особенностях работы таймеров. Если задать частоту ниже 32 МГц, то ощутимо теряется точность сигнала. Например, для 8 МГц: Для получения частоты 800 КГц задаётся ARR = 9. Значит регистру CCRx доступны только значения 0..8. А это примерно 100 КГц точности или разброс в 10 мкс, что уже очень критично. Вариант борьбы с этим — уничтожение этой концепции генерации сигнала, переход на ассемблерные задержки или прерывания.  В любом случае, зачастую в проектах используют максимальную тактовую частоту, а снижают её только для энергосбережения, когда светодиоды уже должны быть выключены.  Другой вариант — использование отдельного МК, например F0 или G0, как UART/SPI/I2C -> ARGB драйвер. Такие проекты уже существуют.   Мой выбор — принять все ограничения, а для open-source сделать пометку. Максимальная частота обновления адресной ленты упирается напрямую в протокол. Посчитаем предел для 25 FPS.  25 Гц -> 40 мс = 40.000 мкс. Передача для 1 диода занимает 30 мкс.Таким образом, предельное значение — порядка 1300 шт. https://crazygeeks.ru/stm32-argb-lib/ https://github.com/Crazy-Geeks/STM32-ARGB-DMA https://www.thevfdcollective.com/blog/stm32-and-sk6812-rgbw-led https://narodstream.ru/stm-urok-119-ws2812b-lenta-na-umnyx-svetodiodax-rgb-chast-2/ https://cdn-shop.adafruit.com/datasheets/WS2812.pdf https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf http://www.normandled.com/upload/201808/WS2815%20LED%20Datasheet.pdf https://cdn-shop.adafruit.com/product-files/2757/p2757_SK6812RGBW_REV01.pdf  Студент