Аппаратная ШИМ в микроконтроллере ATmega8
В этой заметке я постараюсь немного затронуть тему аппаратной ШИМ (широтно-импульсной модуляции, англ. PWM - Pulse-width modulation) в микроконтроллерах семейства AVR на примере микроконтроллера ATmega8. Классический ШИМ сигнал представляет собой цифровой сигнал, определенной постоянной частоты.
Меняться в нем может скважность - длительность состояния логической единицы в периоде сигнала. Например, на рисунке внизу показаны разные ШИМ сигналы, скважность которых увеличивается с верхнего графика к нижнему:
ШИМ совместно с RC цепочкой используется для генерации аналогового сигнала, а если позволяет частота – то я для воспроизведения звука. Мое первое столкновения с ШИМ произошло, когда я захотел плавно менять яркость мощного одноваттного светодиода. После ШИМ помогла решить проблему управления скоростью вращения двигателя постоянного тока, и управления цветом RGB светодиода.
Для демонстрации работы с ШИМ напишем программу, которая будет плавно менять яркость светодиода от нуля до максимума, а потом плавно ее снижать. То есть нам нужно сгенерировать примерно такой ШИМ сигнал (масштаб не соблюден):
Генерировать сигнал будем аппаратными средствами микроконтроллера ATmega8. Можно конечно сгенерировать все и программно, но это не экономично и неудобно, если микроконтроллер должен выполнять что-то еще, кроме генерации ШИМ сигнала. Принципиальная схема макета:
На схеме показана стандартная обвязка ATmega8. Q1 - любой pnp транзистор, способный выдержать ток 500мА, я использовал биполярный pnp транзистор ZXTN19020, так как у него очень малое сопротивление открытого канала коллектор-эмиттер, всего 18 миллиом и при токах 500ма он не будет ощутимо греться. Транзисторный ключ открывается при высоком уровне на PB1 и пропускает через цепочку резистор R4-коллектор-эмиттер-светодиод ток, который по закону Ома равен I=5B/R4. (сопротивлением светодиода в рабочем состоянии и канала коллектор-эмиттер на транзисторе Q1 мы пренебрежем).
Зададимся, что наш светодиод во включенном состоянии будет питаться током 500мА (максимальный ток для светодиода Cree MC-E, используемого мною в этом макете, при последовательном соединении 4-х кристаллов составляет 2.8А), для этого вычислим номинал ограничительного резистора: R4=5В/0,5А=10 Ом. R3 номиналом 1 кОм. Следует не забывать про охлаждение светодиода, иначе очень быстро сгорит. Для небольших мощностей достаточно использовать радиоатор и термопасту. Теперь перейдем к написанию прошивки:
#include <avr/io.h>//библиотека ввода/вывода //Програма задержки void pause (unsigned int a) { unsigned int i; for (i=a;i>0;i--); } //Програма инициализации ШИМ void init_pwm (void) { TCCR1A=(1<<COM1A1)|(1<<WGM10); //На выводе OC1A единица, когда OCR1A==TCNT1, восьмибитный ШИМ TCCR1B=(1<<CS10); //Делитель= /1 OCR1A=0x00; //Начальная яркость нулевая } //Основная програма int main(void) { unsigned char i; DDRB=0x02; //Инициализация PB1 (OC1A) как выход init_pwm(); while (1) { for (i=0;i<255;i++) //Плавно повышаем яркость {OCR1A++; pause(1000);} for (i=0;i<255;i++) //Плавно понижаем яркость {OCR1A--; pause(1000);} } return 1; }
Рассмотрим приведенный выше код.
Сначала мы инициализируем ШИМ, после чего в вечном цикле постепенно увеличиваем-уменьшаем яркость светодиода.
Рассмотрим подробнее инициализацию ШИМ.
Будем использовать так называемый Phase correct PWM на таймере 1. Счетчик TCNT1 постепенно увеличивается (согласно установленному делителю), когда его содержимое становиться равным содержимому OCR1A, то на выводе OC1A в зависимости от битов COM1A0, COM1A1 (в регистре TCCR1A), устанавливается нолик или единица. После счетчик достигает 0xFF (в зависимости от битности ШИМ) и начинает уменьшатся. Как только TCNT1 снова сравняется с OCR1A, на пине OC1A уровень меняется на противоположный. После чего счетчик достигает 0х00 и все повторяется снова.
В нашем случае COM1A1=1,COM1A0=0. И это означает, что при начале счета, на выводе OC1A устанавливается высокий уровень. При достижении счетчиком значения OCR1A при возрастании уровень на OC1A становится нулевым. А при достижении счетчиком значения OCR1A при убывании, уровень на OC1A становиться высоким, и т.д. Проще говоря: «чем больше значение OCR1A – тем больше заполнение сигнала».
Битность и режим ШИМ задается с помощью битов WGM13-WGM10 (биты WGM13,WGM12 находятся в регистре TCCR1B, а биты WGM10, WGM11 в регистре TCCR1A).
Для нашего 8-ми битного Phase correct PWM требуется, чтобы WGM10=1.
Видео работы программы:
Скачать прошивку для ATmega8, как проект для AVR Studio 4
Пример реализации аппаратной ШИМ в несколько каналов на микроконтроллере ATmega8
пароль от архива скажите
пароль от архива скажите пожалуста
Тут пароль внизу в
Тут пароль внизу в самом:
http://avrlab.com/node/1579
Пример реализации аппаратной ШИМ в несколько каналов на микрокон
Скачал архив с прошивкой, там пароль..какой?
http://avrlab.com/node/1579
http://avrlab.com/node/1579
ШИМ и газоразрядные индикаторы
доброго времени суток )))
начитавшись вашего сайта и я решил сделать нечто подобное....., а именно: нужно одновременно и плавно зажигать цифру 8 и тушить цифру 4, на газорозрядных индикаторах....., когда цифра 8 будет гореть на полгую то 4 потухнет....., далее мы плавно зажигаем 4 и плавнол тушим 8....., ну и так по кругу. для данных целей использовал atmega8 и 2 таймер который считает до 255....,срабатывают прерывания по совпадению и переполнению.
вот код:
только вот сталось не так как гадалось...., в реале, при включении горит на полную цифра 4 а 8 потихоньку набирает яркость...., когда 8 набрала яркость на все 100% то и 4 горит на все 100%, затем 4 начинает плавно тухнуть, при этом 8 горит на полную...., когда 4 потухла 8 горит на полную...., далее 4 набирает яркость,8 горит на полную...., только когда 4 набирет яркость на все 100% вот тогда начинает сбрасывать яркость 8......, и так по кругу........, что в моем коде не верно, из-за чего моя идея не работает ?
как посчитать сколько
как посчитать сколько в данном случае pause(1000); равна пауза ?
1000 машинных циклов, если
1000 машинных циклов, если кварц стоит кратный секунде, то можно высчитать длительность паузы в секундах. В любом случаи все напрямую зависит от источника тактирования. Для чего нужна точность? Что за система будет?
да точность как таковая
да точность как таковая меня и не интересует......, я пишу в СodeVision, а там есть стандартные ф-ции delay_ms()и delay_us(), вот я и спросил для того чтобы знать какое значение ставить в скобках. Я так понимаю все зависит от частоты, если я тактируюсь от внутреннего генератора с частотой 1МГц то один машинный цикл будет равен 1мкс ?
Да, все правильно. Есть
Да, все правильно. Есть операции, они делятся на логические и математические, так вот некоторые выполняются за 1 машинный цикл (операция присвоения и операция суммирования) а некоторые за два и больше (операция деления). У тебя тут только операция суммирования, которая занимает 1 такт, значит скорость работы будет равна частоте тактирующего источника.
а с какой целью
а с какой целью главная ф-ция возвращает 1 ?
Тут эта штука не
Тут эта штука не обязательная, можно удалить и все должно работать и так.
Автор статьи пропал :-)
хороший пример
хороший пример, СПАСИБО, начинаю понимать ШИМ, собственно вопрос: после какой строчки, в проге, регистр TCNT1 начинает считать ?
После выполнения
После выполнения инициализации таймета, то есть после выполнения init_pwm();
Ребята-профи, помогите
Ребята-профи, помогите разобраться немного дальше.
Экспериментирую на таком вот коде уже второй день и пока не могу придумать, как решить свою задачу.
Так, в принципе, все понятно, но задача в том, чтобы переменные PWM-PWM2 могли стартовать (становиться единицами) в любое инициируемое часами время. На данном этапе можно использовать прерывания и не думаю, что в этом будет проблема. Но как дальше сделать так, чтобы для каждого канала ШИМ установить свой интервал изменения скважности, без влияния на другие каналы, не могу дойти. Если устанавливаем отдельные пауза для каждого канала, они наслаиваются одна на другую. Если как здесь в конце цикла, не получается обособленно регулировать время разгорания каждого канала при старте в разное время.
диммер на основе аппаратного ШИМ
Ребята, я новичок в программировании, и уже несколько суток пытаюсь понять, как можно управлять в данной программе длительностью периода ШИМ. Разобрался, что максимальная яркость или скважность шим меняется путем изменения переменной i, это элементарно. Но не могу решить вопрос, как здесь установить длинный интервал разгорания и погасания(например 1-2 часа). Максимум что у меня получилось - это довести данный период до 20 секунд путем увеличения переменной а до 65500. Как я понял это практически максимальное ее значение.
http://avrlab.com/node/52 Вот
http://avrlab.com/node/52 Вот посмотри тут только вместо плевика напрямую светодиод через резистор подключи. Там переменные все расписаны, попробуй покрутиться с ними. Если не выйдет сделать длительную задержку, то вставь в цикл программы подпрограмму задержки, которая формирует нужную тебе паузу.
То есть прикинул, получается
То есть прикинул, получается примерно такой код:
При таких значениях переменных получается задержка в 1 секунду при 8 мгц. Опять же не пойму почему именно при таких значениях. По идее delay_nop это задержка в 1 такт, то есть 0,0075 мкс. Чтобы получить задержку в 1 мс - delay_1ms нужно чтобы контроллер отсчитал примерно 133333 такта (1000мкс/0,0075мкс). Соответственно t должно равняться 133333. Ввел дополнительную подпрограмму delay_mks соответственно t присвоил значение 133. Далее р присвоил 1000 (количество мкс в мс) но уже на этом этапе задержка получается намного больше 1 мс. Что не так? Что я недопонимаю?
Проверь, возможно источник
Проверь, возможно источник тактирования у тебя внутренний а по умолчанию у меги8 по моему 1МГц частота.
Еще может быть связано с разным времененм (кол-вом тактов на выполненеи операций).
Операции типа присваивание, умножение и тд тп разное количество тактов занимают.
хаха. вот поэтому надо знать,
хаха. вот поэтому надо знать, сколько и каких операций и с какими данными выполняется) и сколько тактов они занимают - тогда, вероятно, можно с высокой точностью формировать паузы? а как избавиться от такого дурного тона, и что в нем дурного?
Спасибо за ответ! Теперь
Спасибо за ответ! Теперь стало все более или менее ясно. Получается, что точных задержек таким образом получить не удастся. Придется, наверное использовать таймер-счетчик для создания точных задержек. А длину выставлять количеством миллисекунд посредством переменной long int. Это не будет дурным тоном при программировании?
Если это будет работать, то
Если это будет работать, то со временем можешь конечно этот "дурной тон" переделать :-)
Вот использовал
Вот использовал такую библиотечку.
Сразу скажу, что пишу под mega128.
В дальнейшем в программе можно выставлять значение timerDelayMs (0-4294967295), а это хоть пару месяцев. Только возникает вопрос, чем может быть в дальнейшем чревато выставление задержки, допустим 3600000(1 час)? Прерывания вроде работать будут, процессор вроде не загружается, так как таймер - аппаратная возможность. Какие тут могут быть подводные камни?
Я для длительной задержки
Я для длительной задержки юзаю цикл for
Таким образом вложенность циклов масштабирует объемы паузы.
Не юзаю переменные больших типов, дабы оперативку микроконтроллера не забивать, да и операции с ними дольше по времени. А вообще пока не было такого проекта где надо было гнаться за временем или получать высокую точность :-)
В том то и дело, что у меня
В том то и дело, что у меня основная цель проекта по прерываниям от часов реального времени формировать интервалы генерации ШИМ в строгих временных рамках, заданных и изменяющихся посредством настройки, думаю, кнопками через меню. То есть что-то типа настраиваемого диммируемого таймера на 6 каналов, каждый из которых должен работать обособленно и одновременно. Плюс терморегулирование тоже шим, через датчик ds18b20 и опять же параллельно. Поэтому и такие требования к задержкам. Понимаю, что для новичка, который ни разу вообще не сталкивался с программированием, замахнулся не слабо, а вдруг все получится:) Шесть каналов настроил, вроде пока все нормально, но теперь осталось понять логику выполнения программы, чтобы задать все переменные и на их основе регулировать работу таймеров с шим. Потом останется привязаться к часам, термодатчику, памяти, сделать меню и клавиатуру, в общем за несколько месяцев управлюсь:))
я тоже пользуюсь таким. вот
я тоже пользуюсь таким.
вот еще бы рассчитать точно, какую задержку дает такой цикл. в 1-wire экспериментальным путем подбирал. еще легко отделался.
еще, где бы сейчас найти пример с таймером, чтобы считывать десятки или сотни микросекунд (нужно уметь считать от сотен миллисекунд до сотен микросекунд - 16-битного таймера хватит для этих целей?
Вот библиотечка, которую я
Вот библиотечка, которую я привел формирует любые паузы. timerDelayMs() здесь принимает в том числе и дробные значения. При timerDelayMs(1) - задержка 1 миллисекунда. При timerDelayMs(0.001) - 1 микросекунда. Пробовал в параллельных этому проектах работает очень точно. Только написана она под 128 мегу. Под другие нужно даташиты смотреть на предмет значения делителя.
наверное, если вопросы
наверное, если вопросы энергопотребления не волнуют = то ничем не чревато.
Как переделать?
Раньше писал на пиках. Атмелы только начинаю мучать. Вопрос такой: "Что нужно поменять, чтобы подключение было анодом к портам, а катоды к питанию подключались, как здесь http://avrlab.com/node/63 ". Просто есть ргб-светодиоды с общим катодом, а достать у нас в городе другое нет возможности. Прога нужна именно с аппаратным шимом. Пишу пока под тини2313, потом к меге16 буду. Как переделать под контроллер я понял, но как переделать, чтобы подключались порты к анодам нет. Можете подсказать? Что-то не увидел комментария моего. Извините, если продублировал
Я делал проект когда-то там
Я делал проект когда-то там фишка была в том, что светодиод был звуцветным. То есть внутри светодиода было два светодиода с разными цветами, оказалось зажечь их можно по одному, так как они были спаяны параллельно но положительный полюс был спаян с отрицательным. Таким образом они зажигались поочередной поачей напряжени с изменением полярности. Так и тут, для подключения трехцветного светодиода с другой полярностью всего-то нужно поменять в программе местами подпрограммы зажигания и гашения светодиода. Пробуй.
Программа задержки
Расшифруйте пожалуйста эту часть программы. Что она делает, на каком этапе выполнения программы она задействованна...
Сделал пример с управлением RgB и еще сделал мигалку милицкейскую, решил их соеденить, как бы 2 режима последовотельно чередующихся, с помощью ШИМ Сначала плавно загорается Красный, пока он тухнет, загорается синий тоже плавно, и так раз 10, а потом переходит к стробоскопическому попеременому миганию красного и синего. Вот решил совместить , но как то не заладилось, пытаюсь понять эти два кода полностью, что бы соеденить их. Работаю с Atmega8515. Полный новичек, но программы переделал под 8515 с помошью datasheet довольно легко.
Заранее благодарен за ответ
Это программа задержки, она
Это программа задержки, она выполняет перебор значения от 0 до заданного. Так же эта функция вызывается записью в программе следующего вида: pause (100); где число 100 - это верхний предел значения для перебора до него. За 1 машинный такт (1 такт генератора тактовой частоты с частотой которого работает микроконтроллер) выполняется 1 присвоение значения переменной i. То есть на выполнение данной программы уйдет 100 тактов. Таким образом мы получим 100 "холостых" тактов, которые будут использованы как программа задержки. Так ясно? Если нет, напиши с какого момента не ясно.
Ну вроде как понятно, на
Ну вроде как понятно, на интуитивном уровне :)
Скажите а мы могли бы заменить эту процедуру путём включения библиотеки задержек delay.h и использованием функции _delay_ms(число миллисекунд)???
И еще один вопросик можно? :
Не очень понятно как работает вот этот механизм:
Сначала присваивается i значение 0, пока i не становится 255 к и прибавляется единица, в какой момент вступает OCR1A++; а затем и pause(1000) ???
И что конкретно означает запись ++ после OCR1A ну и соответственно -- ???
Как я понимаю ++ или -- это соответственно 0х00 (начальное значение OCR1A)+1 и -1, только не понятно как 0х00 доходит до 0xFF что соответствует максимальному напряжению на выходе...
Надеюсь Вы поняли что именно мне не понятно, я сейчас как годичный ребенок, что то хочу, а вот объяснить родителям что именно очень тяжело :)
Заранее благодарен :)
Уже разобрался :) Спасибо.
Уже разобрался :) Спасибо.
Кстати что именно собирал?
Кстати что именно собирал? Пиши заметку в блог что вышло.
Как прошивку в формат .hex
Как прошивку в формат .hex засунуть подскажите, а то не умею!!
hex надо записывать
hex надо записывать программо-программатором, одна из них Uniprof.
я имею ввиду текст прошивки
я имею ввиду текст прошивки который в статье, как конвертировать в hex
Читай заметку как получить
Читай заметку как получить hex файл
Скиньте мне пожалуйста
Скиньте мне пожалуйста прошивку в .hex для atmega 8, я вроде всё сделал а не работает лампочка просто светит....
Так есть же все в архиве с
Так есть же все в архиве с проектом.
спасибо
спасибо
Частота импульсов шим
Как она расчитывается? Можно ее изменять?
А если использовать npn транзистор
К примеру 2SC968
Посомтри его характеристики
Посмотри его характеристики по справочнику если он с указанным в схеме транзистром примерно схож, то можно заменять. Параметры для сравнения: напряжение на коллекторе, ток коллектора, коэффициент усиления.
Доработка под CVAVR
Здравствуйте.
Спасибо огромное за полковые статьи! Они очень помогают мне разобраться в сути МК.
Скачал я прошивку - всё работает.
Хочу на основе вашей ШИМ сделать свою мелкую программку, но вот AVR Studio 4 мне очень неудобна, да и матюгается на обыденные вещи.
Больше мне по душе CVAVR, но он что-то ругается на COM1A1 (строчка 13) и CS10 (строчка 14).
Пробовал я подкидывать библиотеки от AVR Studio - только больше ошибок.
Помогите разобрать в проблеме. Если можно, поправьте программу для CVAVR. Буду очень признателен.
Спасибо!
Это конфигурирование
Это конфигурирование таймера-счетчика. Попробуй создать новый проект с КодВижине и запусти конструктор проекта, выбери настройки тайме в соответствии с комментариями возле кода.
Вот может так будет проще и
Вот может так будет проще и понятней, я своё время именно примерно из такого графика понял о чем именно так кричат на форумах упоминая про ШИМ :-)
Почему не ЦАП?
оставил комментарий на сайте avrlab.com:
как я понял ШИМ - импульсный ток, не постоянный. Не лучше ли использовать ЦАП - управление амплитудой для управления яркостью?
Ниче плохого не вижу, что
Ниче плохого не вижу, что светодиод работает с импульсным током.
ЦАП можно использовать, как самый простой R2R, но при этом нужно много выводов для управления (минимум 2 шт. если юзается i2c ЦАП), нужен сам ЦАП. Зачем придумывать велосипед, когда есть встроенный аппаратный ШИМ на одной ноге?
ИМХО: в какой-то аудиоаппаратуре заморачиваться с ЦАПом стоит. А простое управление движком, светодиодом можно сделать и на ШИМ без каких-то ощутимых издержек.
Аппаратная ШИМ
большое спасибо за познавательный сайт!
хочу организовать управления сервами по аппаратной шим. у меня atmega128.
решил начать с простого светодиодика и понять как работает шим.
данная программа на atmega 128 не работает, - на выходе PB1 нет сигнала. почему?
не как не могу разобраться!
с си я только разбираюсь, если можно по подробнее.