Широтно-импульсная модуляция (сокр. ШИМ, от англ. PWM - Pulse Width Modulation) является технологией, позволяющей изменять ширину импульсов в то время как частота следования импульсов остается постоянной. В настоящее время она применяется в разнообразных системах контроля и управления, а также в различных приложениях, таких как, к примеру, управление скоростью вращения двигателей, измерениях, управление мощностью чего-либо, связь и др.
Также при использовании ШИМ можно генерировать аналоговые сигналы используя цифровые сигналы. В данной статье мы рассмотрим основы применения широтно-импульсной модуляции (ШИМ) в микроконтроллере ATmega16 (семейство AVR) на примере изменения интенсивности свечения светодиода. Данный пример уже рассматривался на нашем сайте в этой статье (более подробная и наглядная статья), но здесь материал изложен немного по другому – у вас есть возможность выбрать какой из этих материалов использовать.
Контакты ШИМ в микроконтроллере AVR ATmega16
Микроконтроллер Atmega16 имеет 4 контакта для использования ШИМ модуляции - PB3(OC0), PD4(OC1B), PD5(OC1A), PD7(OC2). Более наглядно они представлены на следующем рисунке.
Также ATmega16 имеет два 8-битных (Timer0 и Timer2) и один 16-битный таймер (Timer1). Для понимания принципов формирования ШИМ мы должны понимать основы работы с этими таймерами. Как известно, частота представляет собой количество циклов в секунду поэтому она однозначно связано зависимостью с временем. То есть чем более высокая частота нам нужна, тем более быстрый таймер мы должны использовать. Чем выше частота ШИМ, тем более точно мы можем управлять ее параметрами.
В данной статье для управления ШИМ в микроконтроллере ATmega16 мы будем использовать его Timer2. С его помощью можно выбрать коэффициент заполнения/скважность (duty cycle) ШИМ в широких пределах. Кратко рассмотрим основы этого процесса.
Что такое ШИМ сигнал
ШИМ – это сигнал с различными интервалами ON и OFF сигнала (различными продолжительностями включения). Время, в течение которого сигнал имеет высокий уровень, называется временем включения (“on time”), а время, в течение которого сигнал имеет низкий уровень - время выключения (“off time”). У ШИМ сигнала есть два параметра, которые мы рассмотрим далее.
Коэффициент заполнения (скважность) ШИМ
Процент времени, в течение которого ШИМ сигнал имеет высокий уровень, называется коэффициентом заполнения (скважностью). К примеру, коэффициент заполнения ШИМ сигнала с периодом (длительностью) 100 мс, в котором в течение 50 мс сигнал имеет высокий уровень и в течение 50 мс имеет низкий уровень, равен 50%. Аналогично, если для такой же длительности (100 мс) сигнал 25 мс остается на высоком уровне и 75 мс на низком, то коэффициент заполнения для такого сигнала будет равен 25%. Для его определения нам необходимо знать только длительность высокого уровня сигнала (длительность низкого уровня легко определить как разность между периодом сигнала и длительность сигнала высокого уровня). Более наглядно ШИМ сигнал представлен на следующем рисунке.
Формула для расчета коэффициента заполнения/скважности (Duty Cycle) выглядит следующим образом:
Duty Cycle (%) = On Time/(On Time + Off Time)
Таким образом, изменяя коэффициент заполнения ШИМ сигнала мы можем изменять интенсивность свечения светодиода.
Выбор режима ШИМ
После выбора нужного нам коэффициента заполнения необходимо выбрать еще режим ШИМ – то есть то, каким образом ШИМ будет работать. Существует три типа ШИМ:
- Быстрая ШИМ (Fast PWM).
- ШИМ с коррекцией фазы (Phase Correct PWM).
- ШИМ с коррекцией фазы и частоты (Phase and Frequency Correct PWM).
Быстрая ШИМ используется в случаях когда нам не важна фаза импульсов, например, управление скоростью вращения двигателя или яркостью свечения светодиода.
Для генерации быстрой ШИМ нам необходимо будет запустить на таймере счет и когда он досчитает до определенного значения сбрасывать значение таймера снова в ноль. Таким образом мы установим период следования ШИМ импульсов. Таким образом мы можем контролировать импульс, устанавливая высокое значение сигнала когда счетчик таймера досчитает до определенного значения. А когда счетчик будет возвращаться в 0 на это время сигнал будет низкого уровня. Таким образом, мы имеем большую гибкость в управлении ШИМ сигналом используя всего лишь один таймер.
Необходимые компоненты
- Микроконтроллер ATmega16 (купить на AliExpress).
- Программатор AVR-ISP (купить на AliExpress), USBASP (купить на AliExpress) или другой подобный.
- Кварцевый генератор на 16 МГц (купить на AliExpress).
- Конденсатор 100 нФ (2 шт.) (купить на AliExpress).
- Конденсатор 22 пФ (2 шт.) (купить на AliExpress).
- Светодиод (2 шт.) (купить на AliExpress).
- Кнопка.
- Макетная плата.
- Соединительные провода.
- Источник питания с напряжением 5 Вольт.
Схема устройства
Схема устройства приведена на следующем рисунке.
Мы будем использовать для формирования ШИМ сигнала контакт OC2 микроконтроллера ATmega16, поэтому соедините контакт Pin21 (PD7) микроконтроллера со светодиодом.
Программирование ШИМ сигнала в микроконтроллере ATmega16 на языке С (Си)
Полный текст программы приведен ниже, а в этом разделе поясним ключевые особенности программы. Видео, демонстрирующее работу схемы, приведено в конце статьи. На видео вы можете видеть как яркость свечения светодиода будет изменяться постепенно в зависимости от изменения коэффициента заполнения ШИМ.
Начать программирование ШИМ ATmega16 необходимо с установки нужного значения регистра Таймера 2 – его биты приведены на следующем рисунке. Изменяя данные биты, мы можем настраивать нужные нам параметры ШИМ.
В регистре Таймера 2 три группы битов:
- FOC2 (Force Output Compare for Timer2) – устанавливается когда WGM биты определяют не режим ШИМ.
- WGM2(Wave Generation Mode for Timer2 – режим генерации волны) – эти биты контролируют последовательность счета, максимальное значение счета (TOP counter value) и какой тип формы сигналов будет использован.
- COM2 (Compare Output Mode for Timer2 – режим сравнения для Таймера 2) – можно выбрать режим с инвертированием и без инвертирования.
Установим биты WGM20 и WGM21 чтобы активировать режим быстрой ШИМ.
1 |
TCCR2 |= (1<<WGM20)|(1<<WGM21); |
Для полной информации относительно возможных режимов ШИМ, устанавливаемых битами WGM20 и WGM21, посмотрите официальный даташит на ATmega16.
Поскольку мы не будем использовать предделитель, запишем в необходимые биты ‘001’:
1 |
TCCR2 |=(1<<COM21)|(1<<CS20)|(0<<CS21)|(0<<CS22); |
Настройки режима сравнения (Compare Output Mode) для режима быстрой ШИМ приведены в следующей таблице.
COM21 | COM20 | Описание |
0 | 0 | Normal port operation, OC2 disconnected |
0 | 1 | Зарезервировано |
1 | 0 | Clear OC2 on Compare match, Set OC2 at TOP |
1 | 1 | Set OC2 on compare match, clear OC2 at TOP |
Увеличиваем коэффициент заполнения ШИМ от 0 до 100% - таким образом яркость свечения светодиода будет изменяться с течением времени. Будем использовать значения в диапазоне 0-255, которые будем передавать в OCR2 – это как раз тот байт, который отвечает за установку того самого значения, которое задает различные продолжительности включения (отношение длительности импульса к периоду повторения) импульсов ШИМ.
1 2 3 4 5 |
for(duty=0; duty<255; duty++) // 0 to max duty cycle { OCR2=duty; //медленно увеличиваем яркость свечения светодиода _delay_ms(10); } |
Аналогичным образом уменьшаем коэффициент заполнения со 100% до 0% чтобы постепенно уменьшать яркость свечения светодиода.
1 2 3 4 5 |
for(duty=0; duty>255; duty--) // max to 0 duty cycle { OCR2=duty; // медленно уменьшаем яркость свечения светодиода _delay_ms(10); } |
Полный текст программы
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 29 |
#define F_CPU 16000000UL // задаем частоту микроконтроллера #include "avr/io.h" #include <util/delay.h> void PWM_set(){ // задаем настройки ШИМ DDRD |= (1<<PD7); //устанавливаем PD7 как выход ШИМ TCCR2 |= (1<<WGM20)|(1<<WGM21); //выбираем режим быстрой ШИМ TCCR2 |=(1<<COM21)|(1<<CS20)|(0<<CS21)|(0<<CS22); //clear OC2 on compare match } int main () { unsigned char duty; PWM_set(); // вызов функции установки параметров ШИМ while (1) { for(duty=0; duty<255; duty++) // 0 to max duty cycle { OCR2=duty; // медленно увеличиваем яркость свечения светодиода _delay_ms(10); } for(duty=255; duty>0; duty--) // max to 0 duty cycle { OCR2=duty; // медленно уменьшаем яркость свечения светодиода _delay_ms(10); } } } |
ДОБРЫЙ ДЕНЬ! КАК ПОЛУЧИТЬ СВЕРХ НИЗКУЮ ЧАСТОТУ ПЕРЕКЛЮЧЕНИЯ НА ВЫХОДЕ 0С1А НУ К ПРИМЕРУ 0,1ГЦ? ЕСЛИ В СЕКУНДУ ТО ЭТО РАЗ 10 СЕКУНД .
Добрый вечер. Ну если вы умеете, к примеру, делать частоту переключения равную 10 Гц (с помощью таймера), то сделайте в программу переменную-счетчик, которую инкрементируйте каждый раз при срабатывании прерывания от таймера, а когда ее значение достигнет 100 - обнуляйте ее и делайте необходимое переключение состояния контакта микроконтроллера. Таким образом, вы из частоты 10 Гц получите частоту 0,1 Гц. Надеюсь, мысль понятна
Как фьюзы выставляли?
Что-то мне подсказывает. Что второй цикл for должен выглядеть вот так:
for(duty=255; duty>0; duty--) // max to 0 duty cycle
{
OCR2=duty; // медленно уменьшаем яркость свечения светодиода
_delay_ms(10);
}
Да, спасибо что заметили опечатку, сейчас исправлю
"TCCR2 |=(1<<COM21)|(1<<CS20)|(0<<CS21)|(0<<CS22);"
В этом случае таймер будет остановлен вовсе.
Ошибка в описании и в исходнике!
Хорошо, обоснуйте вашу точку зрения