Так получилось, что до последнего времени я занимался «не
микропроцессорной» электроникой. Но на нынешнем месте работы мне, к счастью,
пришлось заниматься процессорами плотно. С программами, написанными на различных
языках программирования, с различными структурами: 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% ресурсов процессора.
Приводить
схему подключения я не вижу смысла. Просто процессор и несколько светодиодов.
Видео
бегущих огней с моргающим светодиодом привожу как пример.
Если
кому захочется запрограммировать интересный эффект, присылайте управляющий код
и (или) видео работы.
Архив с проектом доступен по следующей ссылке.
Комментариев нет:
Отправить комментарий