Например TDA7294

РадиоКот > Схемы > Светотехника > Бегущие огни и световые эффекты

Автомат световых эффектов на AtTiny2313.

Автор: GARMIN
Опубликовано 10.12.2013.
Создано при помощи КотоРед.

Так получилось, что до последнего времени я занимался «не микропроцессорной» электроникой. Но на нынешнем месте работы мне, к счастью, пришлось заниматься процессорами плотно. С программами, написанными на различных языках программирования, с различными структурами: AVR, ARM и т.д.
Как упражнение в программировании, мне очень помог один проект, который я представляю вашему вниманию.
Автомат световых эффектов с плавным независимым управлением яркостью до 18 каналов (на все ножки, можно больше), с произвольно легко изменяемой программой, на самом простом чипе за 10 гривен?

Вполне возможно.

 Причём остаются ресурсы для дополнительных задач. Такой автомат понадобился для управления рекламными световыми табло, и было необходимо легко изменять как количество каналов, так и  сами световые эффекты. Вначале это была простая программа с ШИМ модуляцией, но по мере увеличения количества каналов процессор просто не успевал отработать программу за период одного прерывания с требуемой частотой (32 кГц). Тогда я вместо ШИМ применил BAM модуляцию. Это упростило требования к процессору и теперь затраты времени на формирование ШИМ не зависят от количества каналов управления. Но я столкнулся с известным эффектом моргания при переходе уровня с 7F на 80, а также при переходе уровня менее значащих бит (3F – 40, 1F-20). Этот эффект устранён модификацией BAM.

Из особенностей проекта отмечу следующее:
- возможность гибко строить программу управления, не изменяя сам код проекта;
- использование простого макроязыка описания программы;
- использование циклов с вложением до 4-х уровней;
- возможность произвольной настройки конфигурации выводов для простой разводки платы;
- использование произвольного числа каналов управления;
- использование модифицированной BAM модуляции с устранённым эффектом перехода 7F-80;
- устранена проблема полной яркости ШИМ (см. далее в тексте);

Проект выполнен в среде CodeVisionAVR 2.05 и написан на С.

Для того, чтобы можно было написать управляющую микропрограмму, использован интерпретатор команд. Причём возможно очень легко изменить его код при переходе с управления восемью каналами на шестнадцать или восемнадцать каналов, не изменяя остального кода программы. Интерпретатор короткий и понятный.
Система команд следующая.
Каждая команда представлена четырьмя байтами во флеш памяти. Основная команда – установка выходов процессора. Первый байт такой команды имеет значение от 1 до 255.
Значение байтов команды:
      Первый байт - длительность свечения в 1/50 сек (20 мс);
      Второй байт – битовая маска конфигурации выходов (87654321);
      Третий байт – битовая маска ШИМ регулирования (1-ШИМ, 0-нет шим) (87654321);
      Четвёртый байт - скорость изменения ШИМ за 1/25 сек
Команды управления:
      Первый байт = 0 – признак управления
      Второй байт = 0 - возврат на начало программы
      Второй байт = 1 – признак начала цикла
                        Третий байт = 0..3 - номер цикла
                        Четвёртый байт = 1..255 - число циклов
      Второй байт = 2 – признак конца цикла
                        Третий байт = 0..3 - номер цикла
                        Четвёртый байт = 0.

Таким нехитрым способом возможно запрограммировать довольно сложную программу управления каналами, где могут одновременно использоваться как плавно зажигающиеся каналы, так и каналы с мгновенным включением – выключением. А также возможно оперативное управление скоростью изменения яркости каналов.
Немного о плавной яркости. Алгоритм управления каналами построен по следующему принципу: Байт конфигурации выходов определяет, светятся ли каналы. Позиции каналов определяются номерами битов в байте.
Если в маске ШИМ регулирования на такой же позиции стоит 0, то данный канал зажигается мгновенно при наличии единицы в байте конфигурации выходов. Единица на нужной позиции в маске ШИМ разрешает плавное изменение яркости канала.
Если в бите конфигурации стоит единица, то за каждый тик (единицу времени в 1/50 секунды) уровень канала увеличивается на величину четвёртого байта команды. Если в бите конфигурации стоит ноль в соответствующем месте, то уровень сигнала уменьшается на величину четвёртого байта команды.
Циклы организованы очень просто. Команда управления со вторым байтом, равным 1, устанавливает счётчик нужного цикла, а команда конца цикла проверяет счётчик на ноль, и если он больше, уменьшает его на единицу и повторяет цикл. То есть цикл организован по принципу
do (i = n)
{…}
while (i--)
, с инициализацией счётчика в начале цикла. Это позволило упростить программу, однако цикл выполняется на один раз больше, чем установлена переменная. Впрочем, это легко поправлено при символьной подстановке.
Команды управления располагаются в массиве данных в начале флеш памяти и имеют много свободного места, поэтому программировать вполне возможно простым редактированием hex кода. После подстановки дефайнами некоторых данных программа стала выглядеть гораздо нагляднее:

flash unsigned char Rules [] = {
DO1(30),
DO0(10),
WAIT(25), CHANNEL(0b00000001), PWMMASK(0b00000000), SPEED(6),
WAIT(25), CHANNEL(0b00000000), PWMMASK(0b00000000), SPEED(6),
WHILE0,
DO0(20),
WAIT(50), CHANNEL(0b00000101), PWMMASK(0b00000011), SPEED(6),
WAIT(50), CHANNEL(0b00000000), PWMMASK(0b00000011), SPEED(6),
WHILE0,
WHILE1,
WAIT(255), CHANNEL(0b00001111), PWMMASK(0b00001111), SPEED(1),
WAIT(255), CHANNEL(0b00000000), PWMMASK(0b00001111), SPEED(1),
GOTO_FIRST
};

Например, приведённая выше программка сначала 10 раз моргнёт каналом номер 1 с периодом в 1 секунду, затем 20 раз моргнет резко третьим каналом и плавно первым (первый канал разгорается и гаснет за 1 секунду), с периодом в  2 секунды. Затем каналы 1-4 плавно разгораются и гаснут в течение 5 секунд. После этого процесс повторяется сначала.

Сам процесс программирования мне показался забавным, вот, например, команды управления бегущими огнями на три канала:

flash unsigned char Rules [] = {
WAIT(12), CHANNEL(0b00000001), PWMMASK(0b00000111), SPEED(22),
WAIT(12), CHANNEL(0b00000010), PWMMASK(0b00000111), SPEED(22),
WAIT(12), CHANNEL(0b00000100), PWMMASK(0b00000111), SPEED(22),
GOTO_FIRST
};

Значение байта скорости изменения яркости можно вычислить следующим образом. Максимальная яркость канала 255. Изменение яркости 50 раз в секунду. Нам нужно полностью засветить канал например, за 2 секунды. Требуемое изменение яркости будет равно 255 / (50 * 2) = 2,55. Ближайшее большее целое будет 3. Яркость канала изменится от минимума до максимума за 255 / 3 = 85 тиков, или за 1,7 секунды.
Кстати, если указать изменение яркости = 0, то все плавно управляемые каналы остановятся на тех уровнях, на которых были во время исполнения команды. а моргать будут только импульсные каналы. Это позволяет сочинять множество интересных эффектов.

Теперь о BAM модуляции. Известный неприятный эффект перехода 7F-80 состоит в том, что при передаче уровня 7F светодиоды зажигаются весь первый полупериод модуляции, а при передаче уровня 80 – второй. Последовательная передача уровней 7F-80-7F-80 приводит к слиянию  пауз и свечения, воспринимаемые глазом как кратковременные погасания и вспышки светодиода. Устранить этот эффект очень просто. Достаточно разбить период свечения старшего бита на несколько частей и воспроизводить его вперемешку с другими битами. У меня применена модификация BAM модуляции с четырнадцатью периодами вместо исходных девяти. Номера используемых битов следующие:

0, 1, 2, 3, 4, 6, 8, 7, 8, 5, 6, 8, 7, 8

Соответствующие им длительности интервалов BAM модуляции:

1, 1, 2, 4, 8, 16, 32, 32, 32, 16, 16, 32, 32, 32

Кстати, многие забывают о передаче паузы в 1 такт перед началом цикла модуляции. Без этого такта яркость свечения светодиодов не соответствуют весу битов. Для примера рассмотрим модуляцию всего двумя битами. Старший бит светится 2 такта, а младший – 1 такт. При передаче четырёх уровней без паузы уровни свечения будут:

0 0 = 0%
0 1 = 33%
1 0 = 66%
1 1 = 100%

Получается, что половина шкалы (10) соответствует 2/3 яркости, а это неправильно. А если добавить один пустой такт, то уровни свечения получатся правильными:

0 0 (0) = 0%
0 1 (0) = 25%
1 0 (0) = 50%
1 1 (0) = 75%

Однако, при передаче максимального уровня светодиод светится только ¾ периода, а не весь. Это дилемма всех способов ШИМ модуляции. Дело в том, что 256 тактов ШИМ модуляции передают 257 возможных комбинаций с учётом всех погашенных и всех зажжённых тактов. Обычно не используют максимальный уровень яркости, так как не заметна разница между яркостью 255/256 и 256/256. Однако, для ключа управления такой режим не слишком хорош. Ведь он всё равно переключается с частотой ШИМ, и на ключе теряется энергия динамических потерь переключения, нагревая его. В данной программе уровень 255 подменяется на уровень 256, позволяя уменьшить потери на переключение при полностью зажженном светодиоде и уменьшая уровень излучаемых электромагнитных помех.

Программа управления светодиодами работает по прерыванию и занимает всего 54 байта:

interrupt [TIM0_COMPA] void timer0_compa_isr(void)
{
      PORTA = BAM_Level_PortA[m];  // вывод подготовленных значений портов
      PORTB = BAM_Level_PortB[m];
      PORTD = BAM_Level_PortD[m];
      OCR0A = TimeArray[n];        // установка нового значения таймера
      n++;
      if (n > 12) n = 0;
      m = NumBits[n];
}

Промежуточной программой между массивами уровней каналов и массивами битовых масок является программа преобразования. В массиве pPorts[] хранятся адреса переменных, выводимых в три порта процессора, а в массиве BitPorts[] хранятся битовые маски, соответствующие номерам выводов в нужном порту. Таким способом можно расписать произвольное количество ножек процессора под управление светодиодами.

void MakeArrayPorts (void)
{
      register unsigned char       *pPort;
      for (i = 0; i < 9; i++)
      {
// обнуление регистров
            BAM_Level_PortA[i] = BAM_Level_PortD[i] = BAM_Level_PortB[i] = 0;
            for (j = 0; j < NumChannel; j++)
            {
                  pPort = pPorts[j] + i;
                  if (i == 0)
                  {
// нулевой слот заполняется только при полной яркости
                        if (CHlevel[j] == Mask_Levels[i])
                              *pPort |= BitPorts[j];
                  }
// установка битов слоёв
                  else if (CHlevel[j] & Mask_Levels[i])  
                  {
                        *pPort |= BitPorts[j];
                  }
            }
      }
}

Вот, собственно, и все премудрости. Весь архив проекта выкладываю в приложении. В проекте есть закомментированные остатки переменных для работы с большим количеством каналов, а также с кнопками изменения скорости работы. Ознакомьтесь и используйте.
Программа занимает 1200 байт и 55 ячеек памяти, использует менее 20% ресурсов процессора. Приводить схему подключения я не вижу смысла. Просто процессор и несколько светодиодов.
Видео бегущих огней с моргающим светодиодом привожу как пример:

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

 

 


Файлы:
Проект автомата световых огней


Все вопросы в Форум.


ID: 1837