ШИМ (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.
И напоследок видосик, как это все должно работать. Успехов в изучении)
Как сделать чтоб шим возрастал и убавлялся при помощи таймера.
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 8000,000 kHz
// Mode: CTC top=OCR1A
// OC1A output: Toggle
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
Код в главном цикле
while(OCR1AH>0x08)
{
OCR1AH = OCR1AH — 0x01;
delay_ms(5);
OCR1AL = OCR1AL — 0x04;
delay_ms(5);
}
while(OCR1AH<0x3E)
{
OCR1AH = OCR1AH + 0x01;
delay_ms(5);
OCR1AL = OCR1AL + 0x04;
delay_ms(5);
В протеусе чуть дергается импульс и все.
слишком быстро увеличиваете, попробуйте убрать все строчки где изменяется OCR1AH
А каким образом можно воплотить программном коде следующее
Включение
Задержка 3сек
1выход Включение постоянно
Задержка 3сек
2-3выход переменное интервалом 1-1,5сек
Задержка 3сек
4выход включение ШИМ 1 (время «разгона» — 50 -60 секунд)
Задержка 20сек
5выход включение ШИМ 2
Задержка 20сек
6выход включение ШИМ 3
Задержка 20сек
6выход включение ШИМ 4
Выключение
4выход выключение ШИМ 1 (время «торможения» 20 -30 секунд)
Задержка 7сек
5выход выключение ШИМ 2
Задержка 7сек
6выход выключение ШИМ 3
Задержка 7сек
6выход выключение ШИМ 4
Задержка 7сек
2-3выход выключение
Задержка 3сек
1выход Выключение
???
Если можно подробно….
у атмеги8 только 3 шим выхода, поэтому начать нужно с выбора соответствующего мк.
Ну пусть будет АТ16
Если есть возможность, с наиподробнейшими коментариями.
Пытаюсь изучить CVAVR, но какая то каша в голове — пока пальцем не ткнут — полный баран
В статьях все есть, переписывать их же в комментарии бессмысленное занятие. Если есть непонятки с тем как работает ШИМ, почитайте тут http://avr-start.ru/?p=1934
Отличный ресурс! Много полезного узнал. Вопрос такой, есть ли возможность в AVR контроллерах организовать считывание ШИМ сигнала извне, и по этому критерию выполнять определённый код?
можете по uart опрашивать мк
Не подскажете, есть ли возможность вывод ШИМ назначить на другую ногу контроллера? Т.к. PB3 используется для компаратора.
Возможно, ищите ножки с подписью OCRn, допустим для первого таймера это OCR1A и OCR1B.
здравствуйте ,как настроить шим чтобы он увеличивался час с нуля до 255 и как запустить еще один шим ? если можно кодом .
проста собираю часы на школьном вольтметре .
Я понял как делать
Здравствуйте.
Здесь выход ШИМ посажен на ногу PB3 (MOSI/OC2)
Поэтому у меня возникло несколько вопросов:
1. Эта нога используется и как выход ШИМ, и как MOSI для прошивки контроллера. Не станет ли это препятствием для внутрисхемного программирования (в том числе повторного)? Ведь у нас эта нога настроена на выход, а в режиме программирования используется в качестве входа.
2. Здесь неоднократно упоминалось, что чтоб разгрузить ногу PB3 можно использовать таймер 1. Есть ли какая-то принципиальная разница в реализации этого варианта, в частности как именно задается выход на конкретную ногу (OC1A или OC1B) ?
когда мк входит в состояние программирования, основная программа не выполняется
имеется ли возможность с помощью шим менять длительность светового импульса с шагом в 5 мс(увеличивать/уменьшать)???
Условно говоря ШИМ меняет яркость, длительность меняется задержками, или таймером
Здравствуйте!
У меня не получается разобраться с симуляцией в Proteus.
Почему-то если подключить к выводу только осцилограф — он показывает только плавное падение уровня с 1 до 0.
Но при параллельном подключении с осцилографом вольтметра ШИМ на осцилографе становится видно.
Так и должно быть или это особенность Proteus?
можно почитать http://avr-start.ru/?p=1934 должно стать более понятно
Да, спасибо. Прочитал.
Я не понял почему в Proteus нужно параллельно с осцилографом вешать вольтметр, чтобы увидеть ШИМ? Без вольтметра не получается.
Сам принцип в изменении заполнения импульса, его видно на осциллографе без вольтметра.
Подскажите, как реализовать двухтактный(двухканальный) ШИМ на меге8.
крутил регистры, таймеры, вн.прерывания, но танец с бубном не проканал((
Нужно, чтобы они пропускали каждый второй свой такт, т.е. высокий уровень никогда не должен быть на обоих выходах одновременно.Один включился, другой-«молчит»,потом первый-«молчит», второй выдает импульс.а ширину их меняем с помощью adc_data[0],для однотактного я делал так:
while (1)
{ OCR2= (adc_data[0]/4);
}
пусть частота неизменна, скажем 125 кгц от 8МГЦ кварца.
Заранее спасибо.
делайте программный шим, достаточно будет одного таймера и двух переменных: 1. отсчитывает период, 2. длительность импульса. при этом можно будет добавить вашу логику.
Широтно-имульсный регулятор интенсивности излучения светодиода, управляемый
переменным резистором. Можно ли узнать как будет выглядеть тогда код в таком варианте? ATMega128
Подключаете АЦП, подключаете ШИМ и делаете. Код будет выглядеть шикарно 🙂
Спасибо за подсказку с программным ШИМ-ом. Одолел. Только скорость очень низкая((((
На частоте таймера 8 МГЦ и длительности импульса в несколько десятков тактовых его длительность (почему-то!!!) аж миллисекунды или сотни мкс.
Так всегда или я в CodeWizard неправильные настройки таймер сделал?
код примерно такой, хотя настройки таймера и сами таймеры пробовал разные:
#include
#include
int takt = 0; /* переменная с каждым тактовым импульсом увеличивается,
а после 64-го обнуляется и увелич. по новой */
int bufer_z; /* переменная отвечает за «заполнение» ШИМ-а*/
int z; /* делит уровень АЦП; по совету с другого сайта
с каждым обнулением переменной takt заносится в bufer_z */
// Timer1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{ takt++;
if ( takt > 64 ) { PORTB.0 = 1;
bufer_z = z;
takt = 0; }
if ( takt > bufer_z ) { PORTB.0 = 0; }
}
#define FIRST_ADC_INPUT 0
#define LAST_ADC_INPUT 0
unsigned int adc_data[LAST_ADC_INPUT-FIRST_ADC_INPUT+1];
#define ADC_VREF_TYPE 0x40
// ADC interrupt service routine
// with auto input scanning
interrupt [ADC_INT] void adc_isr(void)
{
static unsigned char input_index=0;
// Read the AD conversion result
adc_data[input_index]=ADCW;
// Select next ADC input
if (++input_index > (LAST_ADC_INPUT-FIRST_ADC_INPUT))
input_index=0;
ADMUX=(FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff))+input_index;
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
}
void main(void)
{
PORTB=0x00;
DDRB=0x03;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 8000,000 kHz
// Mode: Fast PWM top=0x00FF
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: Off
TCCR1A=0x01;
TCCR1B=0x09;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x01;
OCR1BH=0x00;
OCR1BL=0x00;
TIMSK=0x10;
ACSR=0x80;
SFIOR=0x00;
// ADC initialization
// ADC Clock frequency: 62,500 kHz
// ADC Voltage Reference: AVCC pin
ADMUX=FIRST_ADC_INPUT | (ADC_VREF_TYPE & 0xff);
ADCSRA=0xCF;
// Global enable interrupts
#asm(«sei»)
while (1)
{ z = adc_data[0]/16;
}
}
пожалуйста, направьте в нужное русло, очень нужен импульсный блок питания))
а еще, по-моему Proteus трындит, например delay_ms(1000) длится у него около 650 мс,
а delay_us(4) около 5 мкс.
delay в протеусе норм работает, если выставить правильную частоту. на программном шиме больших скоростей не получишь
вот это печаль! жаль,что сразу не сказали. Но, все же, программный шим тоже полезен.Спасибо.
а если у меня диод на порт D, как его заставить переменно гореть?
Если речь идет про atmega8, то у нее ШИМ каналы помечены OC1A, OC1B, OC2, которые находятся на портуB. На порту D ШИМ каналов нет.
т.е. никак -(((?
Аппаратно никак
подскажите пожалуйста нужно подключить к атмеге8 три светодиода и чтоб каждый постепен плавно включался. как можно организовать.
аппаратно никак, у меги8 только 2 шим выхода
У Atmega8 есть 3 канала ШИМ: два канала на таймере1(ножки PB.1 — OCR1A, PB.2 — OCR1B) и один таймере2(ножка PB.3 — OCR2). Регулируя заполнение ШИМ, мы регулируем напряжение на светодиоде, соответственно его яркость.
ну если вы все знаете, в чем вопрос)
Скажите, почему светодиод до конца не гаснет в вашей программе? Как его полностью тушить?
Вот главный цикл проги с задержками для наглядности.
while (1)
{
while(OCR20x00)
{
OCR2=OCR2-0x01;
delay_ms(5);
}
delay_ms(2000);
};
нужно останавливать таймер