Библиотека WS2812B (NEOPIXEL) для Libmaple с использованием SPI DMA

Rogerclark
Солнце 11 июня 2017 г., 4:24
Я написал библиотеку для управления полосой WS2812B (aka neopixel), используя SPI и DMA.

https: // github.com/rogerclarkmelbourne/ ... 2_libmaple

API основан на библиотеке Adafruit, но большая часть кода была написана с нуля, так как метод работы совершенно другой.

Примечания.

Вероятно, будет работать только с WS2812B, а не с более старым WS2812, из-за ограничений времени, вызванных использованием SPI для генерации импульсного погружения.

Библиотека не имеет всех функций версии Adafruit, E.глин. GetPixelColor, потому что данные Pixel не легко доступны. В долгосрочной перспективе можно было записано GetPixelColor, чтобы прочитать кодированные данные PulsetRain и извлечь цвет пикселей.

Цвет (r, g, b, w) не использует канал W. На данный момент я не уверен, как использовать канал W, и мне нужно будет переоборудовать библиотеку Adafruit, чтобы понять, что делает этот канал.

Нормальные ограничения также применяются к этой библиотеке, e.глин. Теоретически данные требуют логики высокого напряжения 0.7 x vdd. Поэтому, если vdd = 5, VHI должно быть 3.5 В, который больше, чем логика высокой на 3.3V Система. Однако на практике это работает нормально для большинства людей.


Ссылки

https: // cdn-shop.Адафрут.com/dataheets/ws2812.PDF
https: // cdn-shop.Адафрут.com/dataheets/ws2812b.PDF
http: // rgb-123.com/files/ws2812b_vs_ws2812.PDF

Racemaniac
Солнце 11 июня 2017 г. 9:55 утра
отличная работа!
лучше кодировано, чем то, что у меня было до сих пор :).

Всего 2 небольших замечания после того, как выпадали код:
- У вас все еще есть оригинальные комментарии из библиотеки Neopixel выше вашего кода, упоминая AVR и людей, которые написали это: P
- Это исправлено, на каком порте SPI он использует? (Если вы хотите поддерживать несколько портов, наденьте, что разделитель отличается, так как SPI2 работает на половине скорости SPI1 для одного и того же разделителя)

Rogerclark
Солнце 11 июня 2017 г. 10:44
Я подумал, заставить его работать на SPI1 и SPI2, но у меня было только время, чтобы он сегодня работал над SPI1.

Должно быть легко заставить его работать на SPI2, мне просто нужно выработать лучший способ позволить пользователям выбрать его, возможно, иметь канал SPI в качестве второго аргумента в конструкторе и по умолчанию в SPI1.

Я забыл о ограничении скорости на SPI2, но это не будет проблемой, поскольку он работает на Div32 на SPI1, поэтому, если часы SPI2 - половина SPI1, то это должно быть Div16

Re: Ссылки на AVR
Каким -то образом исходный заголовок файла вернулся. (Арргг) Я не заметил этого. Я удалю все этот мусор.

Итак, я прохожу и удалю ссылки на AVR


Еще несколько вещей...

GetPixelColor еще не реализован, так как у меня не было времени написать код для прочтения кодируемого битового шаблона из буфера и конвертировать в RGB. Это должно быть довольно простым, просто прочитав средний бит каждого из триплетов

эн.G Что -то вроде

Возьмите 24 -битную кодировку
очистить декодированный (uint8_t)
для (от 0 до 7)
{
декодированный | = (кодировку>>1 & 0x01)<<8
декодирован>>1
}

Кроме того, я не уверен, что делает параметр w в цвете (r, g, b, w), возможно, для другого светодиода,

Zoomx
Солнце 11 июня 2017 г. 13:15
+1 !!

Rogerclark
Солнце 11 июня 2017 г. 9:59 вечера
Я думаю, что добавлю эту либеральную часть библиотеки Libmaple Core.

Причина этого в том, что он использует SPI.dmasend, которая является специальной функцией, недоступной в других ядрах.

Я также изменюсь, чтобы использовать новую функцию SPI.dmasendasync (), потому что на данный момент нет пользы в использовании SPI DMA, кроме того, что USB работал, потому что мне не нужно отключать прерывания, пока данные отправляются.

Это потребует двойной буферизации, которая займет 18 байтов за светодиод, однако будет достаточно оперативной памяти, чтобы водить машину в 600 светодиодах

Racemaniac
Пн 12 июня 2017 г., 6:13
Rogerclark написал:Я думаю, что добавлю эту либеральную часть библиотеки Libmaple Core.

Причина этого в том, что он использует SPI.dmasend, которая является специальной функцией, недоступной в других ядрах.

Я также изменюсь, чтобы использовать новую функцию SPI.dmasendasync (), потому что на данный момент нет пользы в использовании SPI DMA, кроме того, что USB работал, потому что мне не нужно отключать прерывания, пока данные отправляются.

Это потребует двойной буферизации, которая займет 18 байтов за светодиод, однако будет достаточно оперативной памяти, чтобы водить машину в 600 светодиодах

Rogerclark
Пн 12 июня 2017 г., 7:07
Я провел некоторое время на это сегодня, и двойная буферизация с DMA работает хорошо.

Но он не использует прерывание завершения, поскольку у нас нет никакого кода для этого в настоящее время, так как есть проблемы с обратными обращениями для функций в классах C - хотя @victor_pv расследовал обратные вызовы, и я получил у него личку о это и я надеюсь, что он сможет помочь.

Я добавил новую функцию в SPI под названием dmasendasync (), которая возвращается сразу после начала передачи.
Но я также добавил статический флаг, чтобы указать, была ли передача уже начата, и добавил код в начало функции, которая ожидает завершения передачи, если кто -то находится в процессе (как определено статическим флагом)

Недостатком этого подхода является то, что я не знаю, когда закончится передача, поэтому не может времени снять время сброса 50US.
Однако, как уже отмечали другие люди, время сброса, которое фактически необходимо, составляет всего около 6us, а функция вызывает накладные расходы и DMA quicey и т. Д. Реальные данные, означают, что я получаю около 12us от неизбежного мертвого времени между каждой передачей. Таким образом, нет необходимости добавлять код, чтобы определить, было ли достаточное время сброса разрешено.



На самом деле, это потребовалось несколько часов, чтобы получить эту работу, потому что двойная буферизация вызвала всевозможные странные эффекты при запуске стандартного примера, такого как Colorwipe ()

Сначала я думал, что испортил буферные указатели, или, возможно, я писал в тот же буфер, который был и т. Д

Однако после нескольких часов попытки отправить все нули или все и т. Д., чтобы подтвердить, что ни одно из вышеперечисленных не было....

Я понял, что светодиодные эффекты, такие как Colorwipe (), не создают данные для всей полосы светодиодов (30 в моем случае), они часто просто модифицируют 1 или 2 светодиода, и, поскольку у меня было 2 отдельных буфера, эффекты опирались на Данные из Show () из 2 кадров назад

Эн.глин. Colorwipe () красного (0 = выключен) выглядит как

OOOOOOOOOO
Roooooooooo
Rroooooooo
Rrroroooooo
Rrrrroooooo

И т. д

Но потому что у меня есть 2 буфера, с которыми я оказался

OOOOOOOOOO (buffer 1 and 2 initial all off)
Roooooooooo (буфер 2)
Oroooooooo (буфер 1 плюс r во втором светодиоде)
Rorooooooo (буфер 2 плюс r в 3 -м светодиоде)
Ororoooooo (буфер 1 плюс r в 4 -м светодиоде)
и т. д
Так что в итоге я поочередно вспыхивал красные светодиоды

В любом случае, решение состоит в том, чтобы копировать буфер каждый раз, когда он отправляет через SPI. Это тратит немного времени, но я делаю буферную копию после того, как DMASendAsync был вызван, так что она сделана параллельно с отправленными данными в светодиоды

Я мог бы сделать копию памяти в DMA, но я думаю, что для небольшого количества байтов E.G 30 светодиодов = 272 байта, что время настройки, вероятно, не стоило бы того. И может быть лучше кодировать его в встроенном ассемблере.


КСТАТИ. Я также сделал некоторые улучшения скорости, сделав один LUT, а не 3 (R, G, B) LUT, так как это позволяет увеличить указатель при чтении кодированных значений, а также выходной позиции


Я обновил свой репо github, и я также добавил его в основные библиотеки Libmaple F1, поэтому, если вы хотите попробовать, пожалуйста, загрузите последнюю версию Core From Github

Racemaniac
Ср 14 июня 2017 г. 14:55
Я только что понял, что настроил другую двойную буферизацию для своих проектов:
У меня был буфер, в который вы пишете, который просто содержит реальные значения, а не неопиксельные сигналы. Запуск отправки затем преобразует этот буфер в более крупный сигнальный буфер неопикселя, который затем будет отправлен через DMA.
Это, конечно, также более медленный подход, так как перед отправкой у вас все еще есть все усилия по конверсии, но нет проблем с синхронизацией/трудностями :).

Rogerclark
Ср 14 июня 2017 г. 22:11
@racemaniac

У меня была такая же мысль о том, чтобы сделать конверсию, перед отправкой, но у меня есть чувство, что будет медленнее.

Мой последний код требует 990NS, чтобы установить кодированный пиксель и около 660 Н.

Я не рассчитывал, сколько времени потребуется, чтобы скопировать закодированный буфер, но это будет меньше, чем время кодировать все пиксели.

Единственный способ, которым я могу увидеть, чтобы ускорить это, - это отслеживать, какие пиксели в RGB (некодированный буфер) изменились, а затем кодируют эти пиксели перед передачей.

Вы можете легко использовать это, используя флаг в верхнем байте RGB в качестве его uint32

Но это было бы быстрее только тогда, когда нужно обновлять всего несколько пикселей.

Я думаю, что у Рика есть хороший момент, когда STM32 не очень хорош для этого приложения. Другим MCU, который у меня есть, что может быть полезно для этого, являются Nordic NRF51 и NRF52, так как у него есть отдельный аппаратный раздел, называемый PPI, который может делать всевозможные модные вещи на основе таймеров и DMA.
Но я не разработал, используя PPI, и у меня сейчас нет времени, чтобы узнать об этом :-(

Ореховый
Ср 21 июня 2017 г. 10:10
Идентификатор мысли добавить мои две лакомые кусочки, в моем проекте Speedo я широко использовал портированную версию библиотеки, и даже там функция Get Pixel Color не была идеальной, потому что были хранятся данные в массиве после добавления изменений значения яркости...

Простое исправление для этого и для того, чтобы сделать здесь пиксельный цвет. Было создать первичный массив, в который вы помещаете все свои значения цвета, а затем запускаете цикл, чтобы перенести эти данные в функцию цвета светодиодного набора.

Только в недостатке этого было по сути, вы удвоили использование светодиодного барана...

Rogerclark
Ср 21 июня 2017 г. 11:19
@nutsy

Спасибо.

Я не думаю, что есть простое или идеальное решение для использования этих устройств

Я не убежден, что мой метод-это хороший, так как зависит, сколько пикселей вы измените перед вызовом Show (), лучше ли производительность или хуже, чем делать это битовым путем

Racemaniac
Ср 21 июня 2017 г. 11:31
Основная проблема для такого рода вещей заключается в том, что у каждого есть свои собственные требования/приоритеты. Для моей реализации я отказался от скорости передачи & память, чтобы сделать здание как можно быстрее, а. Как видно в тестах, которые я сделал на DMA, даже если отправка теперь на 33% медленнее, это не будет иметь значения для всех других вещей, которые я делаю, пока DMA работает, и все еще достаточно быстро для количества светодиодов Я буду контролировать :). Так что для меня это был оптимальный компромисс. Но, конечно, для каждого пользователя эти компромиссы будут разными.

К счастью, для большинства пользователей, которые не хотят получать все мощности от микроконтроллера, независимо от того, какая вы выберете, не будет иметь значения, все, что их волнует, - это то, что неопиксели & Бег через 10 минут XD.

Rogerclark
Ср 21 июня 2017 г. 22:51
[Racemaniac - Ср 21 июня 2017 г. 11:31] -
К счастью, для большинства пользователей, которые не хотят получать все мощности от микроконтроллера, независимо от того, какая вы выберете, не будет иметь значения, все, что их волнует, - это то, что неопиксели & Бег через 10 минут XD.
Это правда..

Единственная причина, по которой я начал смотреть на использование SPI DMA, заключалась в том, что библиотека с битой, которую кто-то другой пытался добраться до STM32, не работает для меня.
Поэтому мне пришлось сначала исправить это, а потом заставило меня задуматься, потому что он зацикливается на ассемблере для времени (что, похоже, не дает постоянного времени)
Следовательно, почему я думал, что SPI, который имеет фиксированную частоту часов, может быть лучше.

Если у меня появится шанс, я постараюсь сделать несколько времен в своей версии SPI, потому что в настоящее время я не знаю, сколько времени займет Memcpy, поэтому я не знаю эффективность своей системы

Racemaniac
Чт 22 июня 2017 5:41
Действительно, я также думаю, что версия SPI DMA будет самой простой в использовании, она не зависит от критических времен/отключения прерываний/... это сделает библиотеку намного проще в использовании.