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 Кб)

Теги: