ШИМ (PWM) — широтно-импульсная модуляция. Не нужно пугаться данного термина. Это всего навсего способ регулирования напряжения. Допустим подсветка монитора горит слишком ярко, вы меняете яркость. А что же происходит в этот момент на самом деле?
Представим себе, что подсветка монитора это несколько светодиодов. Питается все это дело от постоянного напряжения. Но вот нам понадобилось уменьшить яркость монитора. Логично ответить, что это можно сделать переменным резистором. На маленьких токах — возможно. Но на больших, резистор будет сильно греться. Сильно возрастут габариты, потери, энергопотребление.
Поэтому люди придумали схему на транзисторах, которая делает из постоянного напряжения пульсирующее. Оказывается, пульсирующее напряжение, в зависимости от заполнения периода будет эквивалентно постоянному напряжению. Т.е. если в течение периода напряжение 50% времени было включено, 50% выключено, то эквивалент постоянного напряжения будет равен 50% от номинального.
В цифрах это просто — было 5В постоянного напряжения прогнали через ШИМ — получили 2,5В. Если заполнение импульса равно 75%, то эквивалентное постоянное напряжение будет 3,75В. Думаю идея понятна.
Теперь приступим к практической реализации. Будем при помощи микроконтроллера изменять заполнение от 0 до 100%, потом от 100% до нуля. Конечный результат должен выглядеть так:
Чтобы было более наглядно, подключим светодиод. В результате у нас будет плавно включаться и отключаться светодиод.
Запускаем наш любимый CodeVision. Создаем проект при помощи мастера. В разделе таймеров (Timers), выбираем Timer 2 и выставляем настройки как на рисунке.
Если попробовать сгенерировать проект, то прога может ругнуться. Соглашаемся, ведь у нас нога 3 порта В должна быть настроена как выход.
Приводим код к следующему виду:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <mega8.h> void main(void) { PORTB=0x00; DDRB=0x08; // Timer/Counter 2 initialization ASSR=0x00; TCCR2=0x6C; TCNT2=0x00; OCR2=0x00; TIMSK=0x00; while (1) { }; } |
Уделим внимание строке OCR2=0x00; Эта переменная как раз и отвечает за величину заполнения импульса. Изменяется данная величина от 0 до 255(0хFF), т.е. 255 соответствует 100% -му заполнению (постоянный ток). Следовательно, если нужно 30% заполнение (255/100)*30=77. Далее 77 переводим в шестнадцатеричную систему OCR2=0x4D;
TCCR2=0x6C; Изменяя данную величину мы можем регулировать частоту ШИМ. Величина частоты работы ШИМ кратна частоте, на которой работает микроконтроллер. В проекте использована частота микроконтроллера 8 МГц, частоту ШИМ использовали 125кГц, следовательно делитель равен 8/125=64
0x6C в двоичной системе счисления 1101100, открываем даташит на Atmega8 и видим описание регистра TCCR2, так вот 1101100 последние цифры 100 и отвечают за выбор частоты работы ШИМ
Приступим непосредственно к программе:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #include <mega8.h> #include <delay.h> void main(void) { PORTB=0x00; DDRB=0x08; ASSR=0x00; TCCR2=0x6C; TCNT2=0x00; OCR2=0x00; TIMSK=0x00; while (1) { while(OCR2<0xff) { OCR2=OCR2+0x01; delay_ms(5); } while(OCR2>0x00) { OCR2=OCR2-0x01; delay_ms(5); } }; } |
Код прост до безобразия: сначала в цикле увеличиваем заполнение от 0 до 255(ff), потом уменьшаем от 255 до 0.
И напоследок видосик, как это все должно работать. Успехов в изучении)
смотрите 6 урок там расписано
С ПИД регулятора получаю значение в десятичной системе (от 0 до 255 или от -255 до 255)
Как мне это значение пропорционально перевести в шестнадцатиричное, что бы послать в OCR2?
пропорцией
подскажите пожалуйста как сделать так чтобы при наступлении прерываний ШИМ останавливался а на ножку мк выводился тот коэффициент заполнения который был в момент остановки таймера?
посмотрите в даташите, какой то бит TCCR2 отвечает за вкл/выкл таймера, соответственно внутри прерывания выключайте таймер и смотрите OCR
Скажите а у вас нет урока по использованию программного шим?
Если есть задача управлять 5-8-ю каналами. например поочередно плавно зажигая и гася СвДиоды, как это лучше сделать.
спасибо.
Добрый вечер
в протеусе код программы работает отлично, но стоит использовать ШИМ и протеус начинает виснуть
может ли МК atmega16 выполнять ШИМ параллельно с кодом программы?
может
Подскажите пожалуйста, не могу разобрать ошибку:
adc = (int)read_adc(1); //считываем значение АЦП
adc_f =(int) adc/4; //делим его на 4 и берём только целую часть
adc_in=(char)adc_f; //преобразование типов
if (adc_in>=0 && 18 && 20 && 23 && <=26) Error:invalid expression
{OCR2=19;}
…………
Когда писал так же под тини13 — не было никаких ошибок
надо каждую расписать adc_in>=18 && adc_in>=20 и тп
Спасибо, теперь работает… 😀
Либо возможно скобки расставить:
if (adc_in>=(0 && 18 && 20 && 23) && adc_in<=26)
как регулировать шим с ацп. дайте направление .подскажите.код не прошу это будет не правильно.
как то вы написали это будет шикарный код что вы имели ввиду? заранее говорю спасибо .
ivan, какой шикарный код) у вас есть 1 регистр 8 битный и есть второй регистр 8 битный. В чем проблема читать один и ложить значение в другой?
вы имеете ввиду что есть регистр АЦП читать его и ложить значение в регистр OCR?
я так понимаю.Может я не правильно понимаю?
правильно
а в настройках ацп выставить в одиночном преобразовании или в беспрерывном? Можете ответить? заранее говорю спасибо.
можна ещё один вопрос.В регистре ацп от 0 до 1023=1024 а в регистре таймера 255
это шаг ~~ 4.011 это нужно вводить float вещественное
можете пересчитать пропорцией или взять старшие 8бит регистра АЦП
вот у меня получился вот такой код в протеусе работает только 1 mux 0 не работает работает в инверсси ну так мне нужно.я хочуэту программу использовать в домовом освещении.что можете посоветовать.может тренироватся в железе? заранее говорю спасибо.
* ADC(PWM)_2mega8.c
*
* Created: 17.11.2016 19:05:54
* Author : User
*/
#define F_CPU 8000000UL
#include
#include «pauza.h»
#include
//#include
ISR(ADC_vect)
{
//OCR1A=ADCW/4;
//OCR1B=ADCW/4;
asm(«sei»);
ADMUX |=(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
OCR2=~ADCW/4;
asm("cli");
asm("sei");
OCR1A=~ADCW/4;
ADMUX |=(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(1<<MUX0);
asm("cli");
ADCSRA |=(1<<ADSC);//бит разрешает проводить замер АЦП
}
int main(void)
{
DDRC|=0x00;
DDRB|=(1<<1)|(1<<2)|(1<<3);
PORTB&=~((1<<1)|(1<<2)|(1<<3));
TCCR1A|=(1<<COM1A1)|(0<<COM1A0)|(1<<COM1B1)|(0<<COM1B0)|(0<<FOC1A)|(0<<FOC1B)|(0<<WGM11)|(1<<WGM10);
TCCR1B|=(0<<ICNC1)|(0<<ICES1)|(0<<WGM13)|(1<<WGM12)|(1<<CS12)|(0<<CS11)|(0<<CS10);
TCCR2|=(0<<FOC2)|(1<<WGM20)|(1<<COM21)|(0<<COM20)|(1<<WGM21)|(1<<CS22) | (1<<CS21) | (0<<CS20);
ADCSRA|=(1<<ADEN);//регистр разрешающийи работу ацп
ADCSRA|=(1<<ADIF)|(1<<ADIE)|(1<<ADFR)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//4 бит регистра разрешает прерывание в АЦП 3 бит флаг по прерыванию уст.1
ADMUX |=(0<<REFS1)|(1<<REFS0)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);
ADCSRA |=(1<<ADSC);//бит разрешает проводить замер АЦП
asm("sei");
while (1)
{
}
}
лениво по даташитам лазить и смотреть регистры, как минимум вы запрещаете глобальные прерывания при выходе из прерывания.
Почему плавает уровень при перестроении шим?
спасибо.Оказывается программирование это тоже труд.Энергоёмкий по информации
Я так понимаю то, что Вы называете частотой ШИМ, является частотой работы таймера. Но таймер отсчитывает 255 «тиков» для формирования импульса (выставляя либо 0, либо 1 в порт, в зависимости от значения регистра OCR2). Эти 255 «тиков» являются одним периодом импульса. То есть реальная частота ШИМ будет в 255 раз меньше частоты, выбранной на вкладке Timer 2 и для Вашего случая будет равна 490 Гц. Поправьте, если ошибаюсь.
Да, пожалуй называть частоту ШИМ, частотой, на которой работает таймер, не корректно, но нужно понимать, что эти вещи для атмеги жестко связаны.
Здравствуйте, admin. Пните пожалуйста в нужном направлении основного цикла. Необходимо плавно зажечь светодиод с помощью ШИМ.Здесь понятно всё. Теперь надо его просто потушить кнопкой. Как кнопками плавно увеличить или уменьшить яркость , мне понятно. А вот как мгновенно ШИМ уменьшить до нуля ?
выключить таймер
Спасибо !
Здравствуйте, admin
мне нужна 3фазни сигнал ocr1a OCR1B OCR2 нуштоби регулируемый частота 0…50гц
я никак не палучайетса
http://avr-start.ru/?p=4500, читаем раздел проблемы в конце статьи
Либо возможно скобки расставить:
if (adc_in>=(0 && 18 && 20 && 23) && adc_in<=26)
ИЛЬЯ! Почитайте ритчи и кернигана на досуге. ))))))
Результат выражения во внутренних скобках у вас всегда =1 (или …FFh, зависит от компилятора), adc_in вы сравниваете с единицей в итоге, в первой половине выражения. И потом по И еще раз сравниваете с 26.
Си самый мощный язык, любое выражение в нем имеет физический смысл, написать ошибочное выражение невозможно. Изучайте.
Пардон, с нулем, у вас там в числе параметров ноль по И
Здравствуйте, уважаемый автор сайта!
На базе Ваших статей проектирую устройство, которое на основании информации о напряжении на истоке полевика (т.е. работает АЦП) принимает решение о выдаче на затвор этого полевика управляющего напряжения (работае ШИМ). Но что-то не клеится и напряжение на затворе в итоге улетает в 5В со всеми вытекающими.Что с фильтром на выходе ШИМ, что без него. Могли бы Вы прокомментировать? Архив с исходником и .hex-файлом прилагаю: https://yadi.sk/d/oJlYKt_l3JosEY
Сложно комментировать, не проникнувшись сутью, у вас полевик открывается на кз, затвор не нагружен, току некуда течь, поэтому фильтр и не работает.
Настроил ШИМ на 125кГц(делитель 64) и при частоте мк 8МГц, частотометр выдаёт частоту ~480Гц (И в Proteus и на железе). Где я лажаю?
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: Fast PWM top=0xFF
// OC2 output: Non-Inverted PWM
ASSR=0x00;
TCCR2=0x6C;
TCNT2=0x00;
OCR2=0x00;
OCR2 возрастает по прерыванию(нажатие кнопки).
Нашёл в чём была проблема.
Дело в том что в новом поколении Atmega — другая формула расчёта частоты шима:
Частота мк./(Делитель*256)
При помощи кнопок не могу реализовать плавное изменение частоты. Собираю генератор высокой частоты на любительские диапазоны. Есть к примеру 2 кнопки и частота 7-7100MHz. Как при помощи кнопок очень плавно её менять? Про проблемы и про студентов с таймерами читал, но не могу понять как реализовать это при помощи языка С. Генератор делаю на мега8 с таймером2, выбрал режим СТС с выходом на ногу меги, даже в протеусе оно заработало(т.е загенерило на частоте 7MHz), таймер0 служит для опроса кнопок(тоже работает), пробовал в ОСR2 подставлять значения типа 0x01, но частота начинает делиться на 2, т.е 3.5MHz. Что делать? 🙁
брать плисину, мк уровня атмеги с вашей задачей не справится
Эх жаль, придется на старых добрых лампах делать 😐