Comments 26
Омг, какой говнокод. И лучше делать так.
mask = OUT1|OUT2|OUT3;
if(...) mask &= ~OUT1;
if(...) mask &= ~OUT2;
if(...) mask &= ~OUT3;
PORTx &= ~(OUT1|OUT2|OUT3);
PORTx |= mask;
mask = OUT1|OUT2|OUT3;
if(...) mask &= ~OUT1;
if(...) mask &= ~OUT2;
if(...) mask &= ~OUT3;
PORTx &= ~(OUT1|OUT2|OUT3);
PORTx |= mask;
+1
Допустим, для одного из каналов выставлена длительность 192. На нулевом (по счету) переполнении таймера порт выставляется в единицу, на 192-м — в ноль.
А теперь представим, что на 128-ом интервале мы меняем значение длительности ШИМ со 192 на 64. Что произойдет? Условие установки порта в ноль не выполнится на данном периоде, и будет импульс высокого уровня длительностью 256+64.
Если «удачно» угадать с моментами изменения длительности, можно растянуть этот импульсна несколько периодов насколько угодно.
Как с этим бороться:
1) Самое простое — вместо «counter==buf_lev_ch1» писать «counter>=buf_lev_ch1»
2) Самое правильное — буферизовать значение длительности ШИМ. То есть на каждый канал использовать две переменные: первую юзер меняет, когда ему вздумается, вторая используется в обработчике прерывания, и ей значение присваивается один раз в начале каждого периода.
А теперь представим, что на 128-ом интервале мы меняем значение длительности ШИМ со 192 на 64. Что произойдет? Условие установки порта в ноль не выполнится на данном периоде, и будет импульс высокого уровня длительностью 256+64.
Если «удачно» угадать с моментами изменения длительности, можно растянуть этот импульс
Как с этим бороться:
1) Самое простое — вместо «counter==buf_lev_ch1» писать «counter>=buf_lev_ch1»
2) Самое правильное — буферизовать значение длительности ШИМ. То есть на каждый канал использовать две переменные: первую юзер меняет, когда ему вздумается, вторая используется в обработчике прерывания, и ей значение присваивается один раз в начале каждого периода.
+2
2) Самое правильное — буферизовать значение длительности ШИМ. То есть на каждый канал использовать две переменные: первую юзер меняет, когда ему вздумается, вторая используется в обработчике прерывания, и ей значение присваивается один раз в начале каждого периода.
но ведь именно так и сделано. при
if (++counter==0)
присваюваются значения буферным переменным.+1
так слишком много прерываний — лучше не так делать — отмерять время интервалами 128 64 32 16 8 4 2 1 тик таймера.при переходе через 0 включаем те каналы, у которых есть старший бит в значении яркости, таймер тикает до 128- оставляем только те, у которых установлен 7 бит, устанавливаем прерывание по сравнению на 192 и так далее — получается не шим, но для светодиодов более чем достаточно и даже лучше, чем шим — всего 8 прерываний таймера, никаких почти вычислительных затрат, несущая частота в таком случае будет выше, чем у шима — светодиоды будут меньше мерцать. у bsvi подробней статья на этот счет была
+2
Спасибо за информацию. Интересная идея.
0
Способ называется BAM — Binary Angle Modulation. Изучайте: bsvi.ru/bam-alternativa-shimu/
+1
Делал RGB «лампу настроения» (mood-lamp) с подобным алгоритмом, только на PIC16.
0
Если не требуется, чтобы все ШИМ были синхронны, можно сделать проще.
Создаём для каждого ШИМ типовую структуру (период, вывод схемы и текущий счётчик).
Например, если нужно 9 ШИМ — берём массив из 9 структур.
Запускаем тактовый таймер со счётчиком и прерываниями.
По прерыванию таймера — берём значение счётчика по модулю количества ШИМов. Используя это значение как индекс массива, обрабатываем структуру (ну, как обычно — меняем счётчик, сравниваем с периодом, если нужно дёргаем вывод).
В отличие от вашего кода — тут чтобы добавить ШИМ нужно всего лишь его описать (назначить период и вывод; увеличить общее число ШИМов). Т.е. никакой хардкодной зависимости. Плюс — скорость обработки не зависит от количества ШИМ (покуда в каждое прерывание обрабатываем ровно один). Плюс — ВСЯ обработка находится в прерывании (т.е. в основной программе «вечный цикл» уже не нужен. Можно уйти в спячку).
Чуть сложнее будет с частотой (очевидно — добавили к трём ШИМ четвёртый — и теперь каждому достаётся не каждое третье прерывание, а каждое четвёртое). Но это тоже обходится, при необходимости.
Создаём для каждого ШИМ типовую структуру (период, вывод схемы и текущий счётчик).
Например, если нужно 9 ШИМ — берём массив из 9 структур.
Запускаем тактовый таймер со счётчиком и прерываниями.
По прерыванию таймера — берём значение счётчика по модулю количества ШИМов. Используя это значение как индекс массива, обрабатываем структуру (ну, как обычно — меняем счётчик, сравниваем с периодом, если нужно дёргаем вывод).
В отличие от вашего кода — тут чтобы добавить ШИМ нужно всего лишь его описать (назначить период и вывод; увеличить общее число ШИМов). Т.е. никакой хардкодной зависимости. Плюс — скорость обработки не зависит от количества ШИМ (покуда в каждое прерывание обрабатываем ровно один). Плюс — ВСЯ обработка находится в прерывании (т.е. в основной программе «вечный цикл» уже не нужен. Можно уйти в спячку).
Чуть сложнее будет с частотой (очевидно — добавили к трём ШИМ четвёртый — и теперь каждому достаётся не каждое третье прерывание, а каждое четвёртое). Но это тоже обходится, при необходимости.
0
Ну не знаю, как на счет проще. Мне кажется сомнительной такая выгода.
Похоже, вы не совсем поняли что происходит в программе.
Обработка и так вся в прерывании. Можно смело идти в спячку. Бесконечный цикл лишь для демонстрации работы, в нём постепенно увеличивается коэффициет заполнения ШИМ по всем каналам. Если подключить к выходам светодиоды, то их яркость будет плавно увеличиваться, и гаснуть достигнув максимума. Начальные значения яркости даны со сдвигом, по-этому светодиоды будут разгораться и гаснуть по очереди.
Похоже, вы не совсем поняли что происходит в программе.
Обработка и так вся в прерывании. Можно смело идти в спячку. Бесконечный цикл лишь для демонстрации работы, в нём постепенно увеличивается коэффициет заполнения ШИМ по всем каналам. Если подключить к выходам светодиоды, то их яркость будет плавно увеличиваться, и гаснуть достигнув максимума. Начальные значения яркости даны со сдвигом, по-этому светодиоды будут разгораться и гаснуть по очереди.
0
В вашем случае: есть три ШИМ. Нужно добавить четвёртый.
Что будете делать? Добавлять по несколько строчек обработки в каждое зависимое место программы, перекомпилировать и перезаливать прошивку?
Ещё потенциальная проблема (может быть не актуальна на AVR, но в общем достаточно часта) — код с кучей условий (ветвлений) практически невозможно предсказать и правильно закэшировать. Линейная обработка может оказаться гораздо быстрее.
Что будете делать? Добавлять по несколько строчек обработки в каждое зависимое место программы, перекомпилировать и перезаливать прошивку?
Ещё потенциальная проблема (может быть не актуальна на AVR, но в общем достаточно часта) — код с кучей условий (ветвлений) практически невозможно предсказать и правильно закэшировать. Линейная обработка может оказаться гораздо быстрее.
0
В моем конкретном коде — дa, добавить пару строк, ввести дополнительно две переменные, изменить строку с заданием маски выходов. Про применение массивов я упомянул, при большем числе каналов — есть смысл, и будет меньше манипуляций при добавлении каналов.
Перекомпилировать и перезалить прошивку. (а без этого никак, в любом случае).
Согласен.
Но если мы говорим о AVR, а тем более о Tiny, то тут, по большому счёту, даже использование высокоуровнего C — перебор. Для примера, если скомпилировать приведенный код, то C-прошивка займет 266 байт, а ассемблерная только 118. А на борту всего лишь 1 килобайт флеша.
Перекомпилировать и перезалить прошивку. (а без этого никак, в любом случае).
Согласен.
Но если мы говорим о AVR, а тем более о Tiny, то тут, по большому счёту, даже использование высокоуровнего C — перебор. Для примера, если скомпилировать приведенный код, то C-прошивка займет 266 байт, а ассемблерная только 118. А на борту всего лишь 1 килобайт флеша.
0
> Перекомпилировать и перезалить прошивку. (а без этого никак, в любом случае)
не-не, если описание ШИМов в массиве — ничего перекомпилировать не придётся!
Дописываем в RAM новую структуру (в простейшем случае однобайтных счётчиков — 3 байта). И меняем байт, где хранится общее число структур. И всё! Никаких прошивок!
не-не, если описание ШИМов в массиве — ничего перекомпилировать не придётся!
Дописываем в RAM новую структуру (в простейшем случае однобайтных счётчиков — 3 байта). И меняем байт, где хранится общее число структур. И всё! Никаких прошивок!
+1
а вообще — на самом деле проще купить микросхему — драйвер светодиодный и не париться — загружаешь в него по i2c или spi значения, а он уже свои шимы включает — можно по разному выходы настроить — тяни толкай, открытый коллектор и т.п., драйвер простенький протаскивает спокойно 500мА тока через одну ногу, разрядность 12бит, количество ног -16, а если это всё по i2c, то на одну шину вешается скока угодно таких микросхем и от контроллера хавается только 2 ноги и i2c, таймеры при этом свободны и процессор не занимается обработкой прерываний.Практического смысла особо нет реализовывать такое на контроллере напрямую, только ради обучения если
0
Может подскажешь несколько конкретных ходовых моделей?
0
PCA9685 — в халявных семлпах NXP например, в Российских магазинах вроде нет. Можно загуглить — шим драйвер светодиодов называется, ну и количество каналов еще указываешь, если мощные светодиоды — вешаешь еще сборки дарлингтона транзисторные дополнительно
0
Да, перед вопросом погуглил, единственное, что нашел, доступнoe — TLC5940 и PCA9635, и то только на ebay.
0
навскидку из доступного: www.compel.ru/infosheet/MBI/MBI5030GF/
0
про 500мА я тут погорячился — просто давно делал свою платку с драйверами для 10 rgb светодиодов — 500мА это со сборками дарлингтона получилось — без них до 100мА протаскивают
0
А можно сделать чтобы ШИМ был не 8-ми битным а к примеру 7 битным? Тогда полагаю можно выиграть в частоте ШИМ.
Собственно это мне сейчас и нужно.
Собственно это мне сейчас и нужно.
0
Можно. Почему бы и нет?
0
А как?
0
Нужно поменять настройки таймера чтобы прерывание генерилось при значении 128, использовать прерывание по совпадению, и, соответственно, подкорректировать значения в обработчике прерывания. Корректировать значения длительности два раза за оборот таймера if (++counter==0 || ++counter==128)
как-то так.
как-то так.
+1
Sign up to leave a comment.
Многоканальный программный ШИМ в AVR