Если вам нравятся светодиоды, особенно RGB, то возможно вы встречались со светодиодами WS2812. Они очень яркие, компактные и дешевые. Эти устройства могут быть соединены друг с другом в бесконечную ленту (если, конечно, хватит питания), поэтому они идеально подходят для создания больших RGB-экранов и матриц.
Но помимо преимуществ у этих светодиодов есть и недостаток: цифровой интерфейс, предназначенный для управления ими, является нестандартным. Этот интерфейс предполагает передачу данных на частоте 800 КГц (1.25 мкс на бит) с различным периодом импульсов, определяющим «0» или «1».


В перерывах между каждой последовательности данных шириной 24 бита имеется задержка 50 мкс, которая нужна светодиоду для обновления своего ШИМ-выхода.
Bit banding
Поскольку протокол для WS2812 не является стандартным, то в обычных микроконтроллерах можно не встретить периферийный модуль, поддерживающий этот интерфейс. Поэтому остается только одно возможное решение – использовать bit banging. Это значит, что микроконтроллер должен определить следующий бит, установит «0» или «1» и вернуть линию вывода в низкое состояние. И все это должно быть проделано за 11.25 мкс.
Временные требования протокола на самом деле будет довольно жесткими для большинства 8-разрядных микроконтроллеров, работающих на тактовой частоте до 16 МГц. В данном случае практически невозможно симулировать протокол без написания программы на ассемблере, оптимизации кода по времени и обращения пристального внимания на количество инструкций (что библиотека Adafruit NeoPixel и делает).
Другой подход
Энтузиаст Elia решил использовать плату STM32VL Discovery для управления светодиодом. Он заметил, что протокол хоть и нестандартный, но похож на обычное ШИМ-управление (управление с помощью широтно-импульсной модуляции). Поэтому он воспользовался своими знаниями о ШИМ и ПДП (DMA или прямой доступ к памяти) для генерации необходимого ШИМ-сигнала.
Трюк с ПДП заключается в том, что байты данных (в данном случае буфер) могут быть переданы из области памяти в регистр сравнения таймера без участия центрального процессора. Контроллер ПДП будет ждать определенного события, в данном случае события, когда счетчик будет равен сравниваемому значению, а затем отправит следующий байт в регистр сравнения таймера.
Быть может поначалу кажется, что ПДП это излишне, но прелесть ПДП в том, что регистр сравнения обновляется до того, как текущий цикл ШИМ завершится, поэтому следующий цикл ШИМ будет уже использовать обновленное эталонное значение, и не будет никакого дублирование битов из-за того, что процессор не в состоянии успевать следить за таймером.
Что делает код:
1. Записывает 8 битов, определяющих R (красный), G (зеленый) и B (синий), в правильном порядке
2. Определяет последовательность требуемых байтов и создает буфер с 24 байтами эталонных величин, формирующих правильную ширину импульса
3. Добавляет 0 байтов в буфер для создания задержки 50 мкс между пакетами данных (длительность импульса = 0)
4. Настраивает размер буфера ПДП, определяет ячейки памяти для буфера и связывает канал ПДП с таймером
5. Запускает таймер, настроенный на генерацию ШИМ-сигнала с частотой 800 КГц и ждет, пока буфер ПДП не станет пустым
6. Останавливает таймер и возвращается в основную программу
Сам код можно найти на ГитХабе.
Видео работы WS2812 с STM32F100:
Перевод © digitrode.ru