PWM або ШІМ на AVR для новачків. Частина 2 - програмний ШІМ
В першій частині статті було розглянуто апаратний ШІМ генератор мікроконтролера. У ньому все добре, але є кілька "але":
- апаратний ШІМ жорстко прив'язаний до певних виводів МК, його неможливо переназначити на іншу ногу
- кількість апаратних ШІМ каналів обмежена, їх кількість залежить від моделі МК
- розрядність апаратного ШІМ неможливо змінити
У цьому випадку може стати в нагоді програмний метод отримання ШІМ сигналу. Він робиться не складно, але вимогливий до частоти роботи мікроконтролера і займає досить багато процесорного часу, на відміну від апаратного, який працює непомітно для основної програми. Але оскільки застосовується він, як правило, для світлодіодних мигалок, то це не так вже й важливо.
Нам необхідно на початку періоду ШІМ сигналу встановлювати певну ногу МК в 1 або 0 (в залежності від того, який сигнал нам потрібен), а потім, по досягненні заданої тривалості імпульсу, інвертувати значення ніжки. Робити це зручніше всього в перериванні по переповненню. Так ми і вчинимо, скориставшись перериванням по переповненню таймера T0. Керувати будемо RGB світлодіодом, тому і назви змінних і макроопределення для портів зробимо зрозумілими.
/*блок дефайнів***************************************************************************************************/ #define RED PORTB.0 #define GREEN PORTB.1 #define BLUE PORTB.2 /*****************************************************************************************************************/ /*оголошуємо змінні********************************************************************************************/ unsigned char red=255, green, blue; //змінні, для зміни скважності ШІМ в програмі unsigned char red_b, green_b, blue_b; //змінні, для буферизації значень скважності ШІМ unsigned char count; //змінна- лічильник викликів обробника переривань unsigned char temp=1; //змінна для роботи алгоритму зміни кольорів /*****************************************************************************************************************/
Коли настає переривання, необхідно збільшити програмний лічильник на 1 і перевірити, чи не переповнився він. Якщо таймер переповнений, то потрібно на всі ніжки, на які виводиться ШІМ, вивести логічну 1, а також зберегти змінні в буфер. Змінні в буфер зберігаються для того, щоб дані про скважність оновлювалися раз на початку кожного періоду, це виключає непередбачувану поведінку виходу. Далі порівнюємо значення лічильника зі значенням буфера скважності кожного каналу. Якщо лічильник досяг цього значення- виводимо в відповідну ногу МК логічний 0.
/*обробник переривання*******************************************************************************************/ interrupt [TIM0_OVF] void timer0_ovf_isr(void) { count++; if (count == 0){ //якщо лічильник переповнився і прийняв значення 0 red_b = red; //зберігаємо значення в буфер green_b = green; blue_b = blue; RED =1; //виставляємо ніжки, що відповідають за ШІМ в логічну 1 GREEN =1; BLUE =1; } if (red_b == count) { RED = 0;} //по досягненні заданої скважності виводимо логічний 0 в ніжку МК if (green_b == count) { GREEN = 0;} if (blue_b == count) { BLUE = 0;} } /*****************************************************************************************************************/
Для демонстрації роботи будемо виводити на світлодіод плавну зміну кольору по кольорах веселки ( Кожен Мисливець Бажає Знати Де Сидить Фазан). Для цього скористаємося нехитрим алгоритмом, який будемо крутити в безкінечному циклі.
/*головна функція*************************************************************************************************/ void main(void) { PORTB=0x08; //конфігуруємо порт DDRB=0x07; TCCR0=0x01; //настраюємо таймер TCNT0=0x00; TIMSK=0x01; //дозволяємо генерацію переривання по переповненню таймера T0 #asm("sei") //глобально дозволяємо переривання /*бескінечний цикл************************************************************************************************/ while (1) { if (temp==1) {if (green < 255) green += 1; else temp = 2;} if (temp==2) {if (red > 0) red -= 1; else temp = 3;} if (temp==3) {if (blue < 255) blue += 1; else temp = 4;} if (temp==4) {if (green > 0) green -= 1; else temp = 5;} if (temp==5) {if (red < 255) red += 1; else temp = 6;} if (temp==6) {if (blue > 0) blue -= 1; else temp = 1;} delay_ms(2); }; /*****************************************************************************************************************/ } /*****************************************************************************************************************/
- Soft_PWM.zip (39 Кб)