mikroPascal for AVR. Урок 7. Аппаратный ШИМ

Практически в каждом своем проекте, вне зависимости от его сложности и предназначения, радиолюбители используют ШИМ. Ниже информация для тех, кто ещё не встречался с такой аббревиатурой. Если же вы уже знаете что это такое, то смело можете пропускать следующий абзац.

Итак, что же такое ШИМ? На английском это сокращение звучит иначе — 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, можно наблюдать такую картину:

На этой ноте я заканчиваю. Надеюсь, что вы нашли в этой статье что-то полезное для себя. Спасибо за внимание!


Прикрепленные файлы:

Добавить комментарий

Ваш адрес email не будет опубликован.