Контроллер для лент на ws2812. 1000 пикселей. Управление по uart. (Читай — USB через ft232).
Перед тем как браться за свою разработку управления лентой на ws2812, просмотрел много реализаций. Можно с помощью dma гнать данные через spi, тратя по байту памяти на каждый выходной бит. Тоже самое можно через таймер с pwm. Есть конструкция на встроенных логических элементах pic-контроллеров. Но там, кажется, без dma. Поэтому поставил себе ТЗ — размер 1000 пикселей, обновление около 30 раз в секунду, Массив данных —
#define LED_MAX 1001 struct RGB { uint8_t Green; uint8_t Red; uint8_t Blue; }; struct RGB OutputArray[LED_MAX];
— по три байта на пиксель. Под это дело, с некоторым запасом по производительности подобрать наиболее дешёвый процессор.
Ещё до прочтения статьи про pic-и, у меня уже была идея прикрутить к выходу spi какую-нибудь элементарную логику для преобразования этого сигнала для светодиодной ленты . Под рукой была 74hc02 это четыре «или». И она прекрасно подошла. Суть идеи какая. Сигнал одного бита для ленты состоит, грубо говоря, из трёх частей. Первая и последняя одинаковые, а средняя меняется в зависимости от значения бита.Для выделения этой части я с помощью таймера сформировал дополнительный импульс. А логика просто выбирает что подать на выход. В начале и в конце бита это сигнал с sck, а в середине, во время импульса, sda. Ещё она служит выходным буфером, согласователем уровней, некоторой защитой.
По фронту sck наступает фронт выходного сигнала, во время высокого уровня OutTIM выход зависит от выхода MOSI.
Теперь к реализации. Частота 52 МГц. С предделителем 64 для spi получается поток данных в ленту 812500 бит в секунду. Ближайшее, что получилось подобрать. Поскольку у нас spi идёт через dma, на кадр (у меня он 3kB,) нужно всего два прерывания.
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi){ HAL_TIM_IC_Stop(&htim3,TIM_CHANNEL_4); HAL_TIM_Base_Start_IT(&htim3); }
Первое случается по окончании массива данных. В нём мы отключаем выход pwm таймера, но не останавливаем его. Это для паузы между кадрами. Когда он дотикает до переполнения случится второе прерывание, которое вновь запустит поток данных и включит pwm.
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi){ HAL_TIM_Base_Stop_IT(&htim3); HAL_TIM_IC_Start(&htim3,TIM_CHANNEL_4); HAL_SPI_Transmit_DMA(&hspi1, &OutputArray[0].Green, LED_MAX*3); }
Всё аппаратно! Ядро включается только в этих прерываниях. Всего 30 раз в секунду. Что интересно, скорость их обработки не критична. Я даже приоритеты не выставлял.
Сначала я пробовал на демо-плате с stm32f103. Когда заработало там, перенёс в самый младший f030f4p6. Потом добавил туда i2c. Потом uart на 256кбод. Всё пашет.. Ещё и место осталось для всяких динамических эффектов. Причём я делал всё через cubeMX, со стандартными библиотеками HAL. Частота у него до 48МГц. На выходе получается 750кГц. Но на ней тоже всё работает. Это очень хорошо и кстати. Так как на этой частоте работает usb периферия в камнях постарше. Если надо больше пикселей, то надо брать контроллер с большим количеством каналов dma.
Был один неприятный момент. После паузы сброса, первый бит данных всегда получался длинным. От чего в первый пиксель попадал 0x80 в зелёный цвет. Хотел было этот диодик ставить на плату. Но потом подправив немного функцию запуска таймера всё заработало.