Используйте PWM и ISR одновременно на AVR

Можно ли одновременно использовать выходы AVR PWM и прерывания ISR? У меня есть проект, который я пытаюсь реализовать на ATMega328P, и мне нужно 3 выхода ШИМ, но ТАКЖЕ нужно иметь возможность использовать прерывания ISR от двух разных таймеров для выполнения некоторого другого мультиплексирования и обработки кнопок (о да, также необходимо использовать внешние прерывания INT0 и INT1).

Есть ли способ сделать оба?

Обновление для уточнения: вот полная настройка. У меня есть 3 светодиода RGB, для которых мне нужен PWM для каждого из их каналов. Этот ШИМ может работать на той же частоте, но требует независимых рабочих циклов для каждого канала, чтобы я мог создать любой цвет, который мне нужен. Поскольку у ATMega328P нет 9 независимых ШИМ, мне нужно его подделать. Итак, мой план состоял в том, чтобы использовать мультиплексирование ШИМ. По сути, установите ШИМ для RGB1, переключитесь на RGB2, а затем на RGB3 со скоростью> 400 Гц. Таким образом, мне нужно только 3 канала ШИМ на чипе, и я могу просто переключать, какой светодиод заземлен (они являются общим катодом). Итак, мне нужно прерывание ISR для обработки самого мультиплексирования, а затем я обычно использую прерывание ISR с более низкой частотой (~ 100 Гц) для обработки нажатий кнопок (в основном мой способ устранения дребезга, я считаю его довольно эффективным). Итак, как видите, мне нужно 3 канала PWM и 2 ISR.

Да, это довольно просто, но эффективность зависит от того, на какой скорости вам нужно, чтобы каждый из них работал ... какие-нибудь подробности о том, что вы пытаетесь сделать?
Может быть, более конкретно, какой частоты (или диапазона частот) должен быть каждый из выходов ШИМ? Какую частоту вы планировали использовать с ISR для обработки кнопок?
См. обновление исходного сообщения.

Ответы (2)

Довольно просто создать свои собственные ШИМ-каналы в программном обеспечении, особенно когда они не очень быстрые - например, то, что вы пытаетесь сделать. Играя с таймерами, имейте в виду, что на самом деле это просто счетчик, уменьшенный по сравнению с тактовой частотой ЦП, который сбрасывается, когда достигает определенного значения TOP . Вы можете использовать один таймер, чтобы делать много разных вещей.

Обзор таймера

ATmega328 имеет три таймера — два 8-битных и один 16-битный. Единственная причина для использования 16-битного таймера — если вам нужно дополнительное разрешение — более точное для ваших идеальных значений частоты и рабочего цикла. 8-битный таймер 0 потребляет наименьшее количество энергии во включенном состоянии, поэтому я рекомендую использовать его как можно чаще. Каждый таймер имеет независимые источники прерываний и выделенные выходы, которые можно настроить на автоматический переход в LO или HI в определенное время, создавая автоматический аппаратный ШИМ. Все, что вам нужно сделать, это обновить время рабочего цикла, а оборудование сделает все остальное за вас. Однако вы можете легко сделать это в программном обеспечении, чтобы создать гораздо больше каналов ШИМ.

Аппаратные соображения

Для начала я предполагаю, что это то, что вы пытаетесь сделать:

Мультиплексирование RGB

Есть три RGB-светодиода. Одна линия управляет каждым из трех внутренних светодиодов, и эта линия используется для каждого светодиода одного цвета. Другая линия управления отвечает за заземление этих светодиодов с общим катодом, всего шесть линий управления. Одновременно горит только один RGB-светодиод, но линии управления с земли перетасовываются достаточно быстро, чтобы этого никто не заметил. Я включил «черный ящик» в качестве основного управления, потому что я не знаю, как вы потребляете ток светодиода. Если вы выберете вариант 1, вывод ввода-вывода станет LO, чтобы потреблять ток для светодиода. При этом помните, что общий ток контактов ввода-вывода составляет 40 мА, поэтому каждый внутренний светодиод может использовать только (40/3) = 13 мА. Вариант 2 преодолевает эту проблему, используя транзистор для отвода тока (также будет работать биполярный транзистор с базовым резистором), однако это переключает управление с активного LO на активный HI, управляя базой/затвором, чтобы поглотить ток светодиода через эмиттер/источник. На каждой линии управления можно использовать один резистор для ограничения тока светодиода, поскольку одновременно будет гореть только один из трех подключенных светодиодов.

Обратите внимание, что при таком мультиплексировании каждый светодиод будет гореть не более 33,33% времени. Яркость каждого светодиода будет меньше ожидаемой, и это также повлияет на цвет RGB.

Рекомендации по ШИМ

Я не могу точно сказать вам, что делать, потому что вы не сказали, какова была тактовая частота вашей системы. Я знаю, что для Arduino это 16 МГц. Тем не менее, чипы AVR по умолчанию используют внутренний генератор 8 МГц, уменьшенный до 1 МГц. Этот предварительный делитель системных часов также можно изменить в программном обеспечении. Это обсуждается в таблице данных в разделе 9.11 «Предварительный делитель системных часов».

У вас есть две частоты, о которых нужно беспокоиться: частота переключения цветов RGB (насколько быстро пульсирует каждый отдельный светодиод) и частота переключения основного управления RGB (насколько быстро переключается управление с одного светодиода RGB на другой). Одна из этих частот должна быть немного выше другой. Я бы предложил более высокую частоту импульсов.

Например: переключение управления светодиодом каждые 2 мс означает, что общий период переключения управления составляет 6 мс, создавая частоту 167 Гц. Частота импульсов должна происходить в течение 2 мс, когда каждый светодиод включен. Сколько должно поместиться в эти 2 мс, зависит от вас, но я бы сказал, по крайней мере, 5. Следовательно, следует использовать частоту импульсов 2,5 кГц (5 / 2 мс = 2,5 кГц). Таким образом, каждый светодиод пройдет 5 полных импульсных циклов в течение 2 мс, когда он включен линией управления с земли. Вы можете поиграть с этими числами, чтобы увидеть, что произойдет...

Программное управление

Как только вы выясните, насколько быстро все должно быть на самом деле, управление программным обеспечением станет относительно простым, но есть несколько разных способов сделать это.

Опция 1

Вы можете настроить один таймер в режиме CTC со значением TOP, установленным путем сравнения соответствия A, чтобы создать более высокую частоту импульсов. В нашем примере это должно быть 2,5 кГц. Каждый раз, когда срабатывает этот ISR, все элементы управления цветом светодиодов должны быть установлены на HI. Переменная в этом ISR также может подсчитывать количество срабатываний. В пятый раз переключите управление с одного светодиода на другой и сбросьте счетчик ISR. Это создает таймер 2 мс внутри ISR 2,5 кГц.

Если для сравнения соответствия A задано срабатывание в начале каждого цикла импульса светодиода, используйте сравнение соответствия B для срабатывания, когда светодиод должен быть выключен (линия управления цветом очищена). Это значение необходимо обновить внутри ISR сравнения соответствия B для следующего светодиода, который необходимо отключить. «Время отключения» можно обновить в файле main.

Я делал это много раз и знаю, что это работает хорошо, но нужно подумать о том, как узнать, какой цвет светодиода отключать, и как установить следующее значение соответствия сравнения B. ISR B должен срабатывать 3 раза (по одному разу для каждой линии управления цветом) каждый раз, когда срабатывает ISR A для запуска нового цикла ШИМ. Я дам вам разобраться в этих деталях...

Псевдокод:

// Every 1 ISR iteration is start of new PWM cycle
// Every 5 ISR iterations is time to switch control to next RGB LED
ISR_A{

  // Time to switch control from one RGB LED to the next
  if(++cycle_count == 5){
  TURN_OFF(ctrl_R | ctrl_G | ctrl_B); // make sure the previous LED is OFF
  cycle_cnt = 0;                      // reset count

  // Switch from LED 1 to LED 2
  if(ctrl_gnd1){
    TURN_OFF(ctrl_gnd1);
    TURN_ON(ctrl_gnd2);
    R_LED_time = R_LED2_ON;
    G_LED_time = G_LED2_ON;
    B_LED_time = B_LED2_ON;
  }
  // Switch from LED 2 to LED 3
  else if(ctrl_gnd2){
    TURN_OFF(ctrl_gnd2);
    TURN_ON(ctrl_gnd3);
    R_LED_time = R_LED3_ON;
    G_LED_time = G_LED3_ON;
    B_LED_time = B_LED3_ON;
  }
  // Switch from LED 3 to LED 1
  else{
    TURN_OFF(ctrl_gnd3);
    TURN_ON(ctrl_gnd1);
    R_LED_time = R_LED1_ON;
    G_LED_time = G_LED1_ON;
    B_LED_time = B_LED1_ON;
  }
  // This is the start of a new PWM cycle, turn on the color control lines
  TURN_ON(ctrl_R | ctrl_G | ctrl_B);
}

// This ISR will trigger when the next LED color control line should be turned off
ISR_B{

// ISR Trigger at RED LED duty cycle
if(COMPARE_MATCH_B = R_LED_time)
  TURN_OFF(ctrl_R);

// ISR Trigger at Green LED duty cycle
if(COMPARE_MATCH_B = G_LED_time)
  TURN_OFF(ctrl_G);

// ISR Trigger at Blue LED duty cycle
if(COMPARE_MATCH_B = B_LED_time)
  TURN_OFF(ctrl_B);

// Need to compare color control times to determine when the next ISR_B should trigger.
// ... Your Algorithm here...
}

Вариант 2

Это может быть более простой вариант, но он требует больше времени для работы часов, а это означает, что системные часы должны работать быстрее ... После того, как вы узнаете, насколько быстрым должен быть светодиодный импульс, определите, какое разрешение управления цветом вы хотите. То есть, чтобы изменить яркость одного светодиода, вы увеличиваете его рабочий цикл на 0,5%, 1%, 10% и т. д. Для простоты я буду использовать разрешение 1%. Это означает, что вам нужно настроить таймер (в режиме CTC) на 100-кратную частоту импульсов...

Этот ISR теперь будет запускаться на частоте 250 кГц. Вы поместите в него переменный счетчик, который считает триггеры ISR. В 0 (или 100, как вы хотите это сделать) это начало/конец цикла PWM, поэтому установите все линии управления цветом в HI. Когда этот счетчик достигнет определенного числа, очистите линию управления цветом с этим конкретным рабочим циклом. Должны использоваться 3 набора значений времени, причем тот, который используется, определяется тем, какой RGB управляется. Этот элемент управления будет переключаться через каждые 100 * 5 триггеров ISR, так как этот ISR происходит в 100 раз чаще, чем вариант 1.

Псевдокод:

// Every 100 ISR iterations equals 1 PWM cycle 
// Every 500 ISR iterations is time to switch control to next RGB LED
ISR_A{

  // Time to switch control from one RGB LED to the next
  if(++cycle_count == 500){
    TURN_OFF(ctrl_R | ctrl_G | ctrl_B); // make sure the previous LED is OFF
    cycle_cnt = 0;                      // reset count

    // Switch from LED 1 to LED 2
    if(ctrl_gnd1){
      TURN_OFF(ctrl_gnd1);
      TURN_ON(ctrl_gnd2);
      R_LED_time = R_LED2_ON;
      G_LED_time = G_LED2_ON;
      B_LED_time = B_LED2_ON;
      break;
    }
    // Switch from LED 2 to LED 3
    else if(ctrl_gnd2){
      TURN_OFF(ctrl_gnd2);
      TURN_ON(ctrl_gnd3);
      R_LED_time = R_LED3_ON;
      G_LED_time = G_LED3_ON;
      B_LED_time = B_LED3_ON;
      break;
    }
    // Switch from LED 3 to LED 1
    else{
      TURN_OFF(ctrl_gnd3);
      TURN_ON(ctrl_gnd1);
      R_LED_time = R_LED1_ON;
      G_LED_time = G_LED1_ON;
      B_LED_time = B_LED1_ON;
      break;
    }
  }

  // Time to turn OFF the red LED control line
  if(++pulse_count == R_LED_time)
    TURN_OFF(ctrl_R);

  // Time to turn OFF the green LED control line
  if(pulse_count == G_LED_time)
    TURN_OFF(ctrl_G);

  // Time to turn OFF the blue LED control line    
  if(pulse_count == B_LED_time)
    TURN_OFF(ctrl_B);

  // This is the start of a new PWM cycle, turn on the color control lines
  if(pulse_cnt == 100){
    TURN_ON(ctrl_R | ctrl_G | ctrl_B);
    pulse_cnt = 0;
  }
}

Сложность этого метода заключается в том, что синхронизация ISR по сравнению с системными часами. При частоте 250 кГц и системной тактовой частоте 16 МГц будет только 64 системных тактовых цикла между каждым запуском ISR. Код должен выполняться вовремя, иначе он не будет работать правильно без значительного снижения частоты импульсов или разрешения рабочего цикла. Поэтому Вариант 1 лучше.

Вариант 3

Если все различные переменные счета кажутся вам запутанными, вы можете попробовать что-то совершенно другое. Используйте 16-битный таймер 1, который имеет 4 возможности прерывания: переполнение, OCR1A, OCR1B и захват ввода ICR1. Когда таймер находится в нормальном режиме, используйте максимально доступный предварительный делитель (1024), чтобы понизить системную тактовую частоту как можно ниже — это составит 15,6 кГц из системных часов 16 МГц или около 1 кГц из системных часов 1 МГц. ISR переполнения является началом цикла ШИМ. Используйте это, чтобы установить все линии управления цветом HI, и подсчитайте эти циклы ISR, чтобы переключить управление со светодиода на следующий. Затем используйте каждый из оставшихся трех источников ISR в качестве рабочего цикла для цветных светодиодов: OCR1A для ctrl-R, OCR1B для ctrl-G и ICR1 для ctrl-B. Эти значения времени могут изменяться когда угодно (в основном или другом ISR) и немедленно обновляются в обычном режиме.

Это не единственные (или лучшие) способы сделать это, но это то, что я успешно использовал в прошлом. Что бы вы ни делали, это реакция светодиода, которую вы должны ожидать:

Сигналы мультиплексирования RGB

Обратите внимание, что каждый светодиод имеет свой собственный рабочий цикл, но все они работают с одинаковой частотой. Каждый RGB включен только 1/3 от общего времени. Вы также можете проверить свои кнопки внутри импульсного ISR. Просто используйте другой переменный счетчик, чтобы создать таймер на 10 мс (вы упомянули 100 Гц) внутри ISR A, аналогичный таймеру, используемому в линиях мультиплексирования наземного управления.

Ваш вопрос не имеет большого смысла. Это все равно, что сказать, может ли мой сосед поливать свой газон, и могу ли я одновременно надеть штаны? Это зависит.

328 имеет 3 таймера: 0, 1, 2. Если ваши 3 ШИМ имеют одинаковую временную базу, т.е. одинаковую частоту, но разные рабочие циклы, тогда да. Вы бы выделили один таймер для генерации 3 PWM, а два других — для генерации ISR переполнения.

INT0, INT1 можно использовать независимо от таймеров, поэтому их можно использовать, если эти контакты свободны.

Я думал, что у каждого из 3 таймеров есть 2 связанных выхода ШИМ? Действительно ли возможно сделать 3 выхода ШИМ только от 1 аппаратного таймера? Это было бы идеально, так как остальные 2 таймера оставались бы свободными для чего угодно.
@AdamHaile Я подозреваю, что здесь предлагается использовать программное обеспечение PWM. Аппаратный ШИМ имеет ограничение в два канала ШИМ, привязанных к определенным контактам и работающих на одной частоте на каждом канале таймера.
Ок... так я и думал. Но могу ли я по-прежнему использовать ШИМ Timer1, одновременно используя прерывания ISR? Я, вероятно, могу работать с обоими, работающими на одной частоте: P
Вы можете настроить ШИМ для переключения, когда счетчик достигает значения сравнения. Это контролирует ваш рабочий цикл. Значение «TOP» и предскаляр счетчика контролируют вашу частоту. Вы можете включить прерывание переполнения, когда ваш счетчик достигает «TOP». Поэтому вы можете использовать ISR переполнения, если нужный период равен 1/(частота ШИМ)