Практически в каждом своем проекте, вне зависимости от его сложности и предназначения, радиолюбители используют ШИМ. Ниже информация для тех, кто ещё не встречался с такой аббревиатурой. Если же вы уже знаете что это такое, то смело можете пропускать следующий абзац.
Итак, что же такое ШИМ? На английском это сокращение звучит иначе — PWM, что расшифровывается как pulse-width modulation (широтно-импульсная модуляция). Вся суть ШИМа в изменении скважности импульсов, что позволяет изменять среднее напряжение на нагрузке. Всего различают три вида ШИМ: аналоговый, двоичный (наш вариант) и троичный.
Рис 1.1. Пример ШИМ.
В микроконтроллерах AVR ШИМ организован аппаратно. Забегая вперед, скажу, что при желании его можно сделать и программно, использовав прерывания. Но это тема следующего урока.
Подведем промежуточные итоги: мы знаем что в МК фирмы Atmel есть аппаратный ШИМ (зачастую не один, а несколько), а так же знаем, что собственно он собой представляет. Но как им управлять?
Оказывается все довольно просто. Для управления ШИМом в mikroPascal есть специальные процедуры, с помощью которых можно инициализировать ШИМ, запустить/остановить и задать длительность импульсов. Далее, предлагаю вашему вниманию простой пример, в котором мы просто запустим ШИМ с заданной длительностью импульса и частотой.
program PWM_mega8_1_2_3ch_def; //Самый простой метод работы с ШИМом в mikroPascal.
//Достаточно просто инициализировать модуль ШИМ и просто
//забыть про него.
procedure Init(); //Для простоты вынес инициализацию в отдельную процедуру.
begin //В этом примере это не существенно, но далее тк будет проще.
DDRB := 0xFF; //Для правильной работы ШИМа необходимо настроить порт «на выход».
PWM16bit_Init(_PWM16_FAST_MODE_8BIT, _PWM16_PRESCALER_16bit_8, _PWM16_NON_INVERTED, 200, _TIMER1); //Инициализация 16 битного ШИМа (OCR1A, OCR1B).
PWM2_Init(_PWM2_FAST_MODE, _PWM2_PRESCALER_8, _PWM2_NON_INVERTED, 200); //Тоже самое, но для 8-ми битного модуля (OCR2).
end;
begin
Init();
while TRUE do begin
end;
end.
Думаю вас заинтересовали функции «PWM16bit_Init» и «PWM2_Init». Это функции из стандартной библиотеки mikroPascal, которая так и называется — PWM Library. Советую вам все-таки почитать даташит для ATmega8 (для демонстрации примеров будет использоваться именно этот доступный МК). И как из последнего следует, в ATmega8 есть три канала ШИМ: один 8-ми битный и два 16-ти битных. Первый работает за счет таймера/счетчика 2, а второй — таймера/счетчика 1. К сожалению, для T0 такой опции нет.
Теперь немного о самих функциях инициализации.
procedure PWM16bit_Init(тип генерации : byte; предделитель : byte; инверсия : byte; длительность импульса : word; используемый таймер : byte);
Для PWM2 отличий мало:
procedure PWM2_Init(тип генерации : byte; предделитель : byte; инверсия : byte; длительность импульса : word);
Отсутствует лишь выбор таймера. Что касается выбора параметров, то для этого нужно открыть help. Там все неплохо расписано, хоть и на английском (в статье я это приводить не буду, уж больно много места это займет).
Теперь можно посмотреть, что получилось в итоге. Для этого запускаем Proteus, выбираем МК.. ну и так далее. Вот результат:
Но согласитесь, довольно редко требуется чтобы скважность ШИМ была неизменной. Ведь практически всегда нужно изменять яркость светодиода и т.д. во время выполнения программы. Так что немного дополним первый пример.
program PWM_mega8_1_2_3ch_def_adv;
procedure Init(); //Инициализация ШИМа в этом примере ничем не отличается от прошлого примера.
begin
DDRB := 0xFF;
PWM16bit_Init(_PWM16_FAST_MODE_8BIT, _PWM16_PRESCALER_16bit_8, _PWM16_NON_INVERTED, 200, _TIMER1);
PWM2_Init(_PWM2_FAST_MODE, _PWM2_PRESCALER_8, _PWM2_NON_INVERTED, 200);
end;
begin
Init();
delay_ms(1000); //Выставляем задержку 1с
PWM2_Set_Duty(128); //Изменяем скважность импульсов для второго модуля ШИМ.
delay_ms(1000); //Ещё 1с задержки. Далее изменяем скважность уже для 1-го модуля ШИМ.
PWM16bit_Change_Duty(128, _TIMER1_CH_A); //Очень интересный момент! Подробнее смотрите в видео!
while TRUE do begin
end;
end.
Теперь, как вы видите, через 1с после запуска программы изменится длительность импульсов на выходе OC2, а ещё через 1с — на выходе OC1A.
Но можно ещё немного усложнить программу. К примеру, добавим выключение ШИМа через 3с после старта.
delay_ms(1000); //Допишем ещё три строчки, которые будут отключать ШИМ (оба канала),
PWM2_Stop; //через 1с.
PWM16bit_Stop(_TIMER1_CH_A);
Этот кусочек кода вставьте перед бесконечным циклом. И вот результат:
Использование библиотек, конечно удобно. Но в любом случае, необходимо уметь работать с регистрами напрямую, что бы не зависеть от других разработчиков (которые как раз и пишут эти библиотеки).
Так как мы используем в качестве «наглядного пособия» микроконтроллер ATmega8, то у ШИМ тут относятся следующие регистры: OCR1AL, OCR1AH, OCR1BL, OCR1BH, OCR2, TCCR1A, TCCR1B, TCCR2. Регистры OCRx служат для записи значения, до которого будет считать таймер, и соответственно это определяют длительность импульса на выходе. Регистры TCCRx служат для настройки режимов ШИМ, настройки таймеров и их предделителей.
В прикрепленном файле TCCR.rar (скачать в конце статьи) привожу «расшифровку» вышеупомянутых регистров
Зададим такие же параметры ШИМ как и в первом примере.
Начнем с TCCR2. Для этого нам нужно:
- Установить режим Fast PWM. Это можно сделать, выставив биты 3 и 6 (WGM21 и WGM20).
- Кроме того, не забываем про бит 5 (COM21), для не инвертированного режима.
- И устанавливаем предделитель на 8 — это бит 1 (CS21).
Должно получиться так: 01101010 (нулевой бит справа).
Далее конфигурируем регистры TCCR1A и TCCR1B.
- В TCCR1A нужно записать такую последовательность: 10100001;
- А в TCCR1B нужно записать: 00001010.
- Что я предлагаю вам туда записать, посмотрите в даташите или картинках выше.
Ниже представлен полный код:
program PWM_1_2_3ch_reg_adv; //В этом примере рассмотрим
//»ручную» настройку ШИМа для
//популярного микроконтроллера ATmega8.
var i: byte;
procedure Init();
begin
DDRB := 0xFF; //Привычно настраиваем порт на выход.
OCR2 := 200; //Но теперь, уже простого заглядывания в «магический» help не будет.
OCR1AH := 0x0; //Нужно брать в руки даташит на тот контроллер, под который вы пишите программу и
OCR1AL := 200; //выискивать раздел с описанием регистров для таймеров.
OCR1BH := 0x0; //В данном случае, нужно было найти информацию по регистрам TCCR2, TCCR1A и TCCR1B.
OCR1BL := 200; //В регистры OCRx записывается число, с которым потом будет сравниваться значение
TCCR2 := %01101010; //таймера, и которое, соответственно будет определять длительность (заполнение) импульсов.
TCCR1A := %10100001;
TCCR1B := %00001010;
end;
begin
Init();
while TRUE do begin
end;
end.
Как уже упоминалось, для изменения длительности импульсов на выходе достаточно просто записать другое число в регистры OCRx. Не забывайте, регистры OCR1AH и OCR1AL представляют собой «половинки» одного 16-ти битного регистра. Можно сразу «закрепить» знания, дописав немного кода:
var i: byte;
….
for i := 0 to 255 do begin //Просто сидеть и наблюдать за неизменной длительностью импульсов
OCR2 := i; //довольно скучно, по этому мы немного внесем разнообразия в этот проектик.
OCR1AL := i;
OCR1BL := i; //Просто возьмем и сделаем так, чтобы сначала длительность импульсов
delay_ms(10); //увеличивалась, а потом уменьшалась.
end; //Это крайне легко сделать с помощью цикла!
for i := 255 downto 0 do begin //Такую конструкцию не очень часто встретишь, но слово «downto» обозначает декремент.
OCR2 := i;
OCR1AL := i;
OCR1BL := i;
delay_ms(10);
end;
Объявите переменную i (byte) и вставьте код в бесконечный цикл. После запуска симуляции в Proteus, можно наблюдать такую картину:
На этой ноте я заканчиваю. Надеюсь, что вы нашли в этой статье что-то полезное для себя. Спасибо за внимание!
Прикрепленные файлы:
- mikroPascal(lessn7_PWM_).rar (1194 Кб)
- TCCR.rar (276 Кб)