Arduino и PWM. Широтно-импульсная модуляция (ШИМ).

Давайте вспомним предыдущую статью о том, как сделать гирлянду на Arduino и попробуем выполнить новую задачу. Думаю, что все видели новогодние витринные гирлянды, в которых плавно мигают светодиоды. Допустим, что мы хотим сделать нечто подобное.
Мы уже рассматривали функцию digitalWrite() и знаем, что значение, которое она записывает, может быть двух вариантов - высокий или низкий уровень. В данном случае нам поможет функция analogWrite(). "Формулировки" функций различаются только начальными приставками, поэтому их легко запомнить.
Функция analogWrite(), так же как и digitalWrite(), содержит в скобках два аргумента и работает по тому же словесному принципу: "куда, что". Главным различием является возможность записи широкого диапазона значений вместо привычного LOW или HIGH. Это и позволит нам регулировать яркость светодиода. Главное замечание, которое необходимо учитывать, это то, что данная функция работает только на определенных контактах. Эти контакты обозначены символом "~". Этот символ означает, что это PWM-контакт. PWM (pulse-width modulation) звучит по-русски как ШИМ (широтно-импульсная модуляция). Принцип работы основан на изменении длительности импульса. Графически это можно изобразить так:


Давайте попробуем разобраться как это работает, рассмотрев простой пример. Для этого необходимо подключить светодиод к PWM-контакту через резистор номиналом 150 Ом и "зашить" в Arduino простенькую программу. Схема подключения и код скетча представлены ниже:


int led = 11;

void setup()
{
  pinMode(led,OUTPUT);
}

void loop()
{
  for(int i=0; i<=255; i++)
    {
    analogWrite(led,i);
    delay(10);
    }
  for(int i=255; i>=0; i--)
    {
    analogWrite(led,i);
    delay(10);
    }
}

Думаю, что в целом код понятен, но необходимо уделить немного внимания циклу for(). Существует такое понятие как разрешение. Поскольку мы работаем с 8-битным разрешением (это будет рассмотрено несколько позднее), то минимальному значению будет соответствовать 0, а максимальному - 255. В конце каждой итерации мы установили временную задержку в 10мс.
Давайте вернемся к схеме из предыдущего урока и попробуем сделать аналогичную гирлянду с использованием функции analogWrite().


int buttonPin = 2;
int pins[] = {3,5,6,9,10,11};

boolean lastButton = LOW;
boolean currentButton = LOW;
boolean enable = false;

void setup()
{
  pinMode(buttonPin, INPUT);
  for(int mode = 0; mode <= 5; mode++) pinMode(pins[mode], OUTPUT);
}

boolean debounce(boolean last)
{
  boolean current = digitalRead(buttonPin);
  if(last != current)
  {
    delay(5);
    current = digitalRead(buttonPin);
  }
  return current;
}

void loop()
{
  currentButton = debounce(lastButton);
  if(lastButton == LOW && currentButton == HIGH)
    {
    enable = !enable;
    }

  if(enable == true)
  {
  for (int i=0; i<=5; i++)
    {
    for (int brightness = 0; brightness <= 255; brightness++)
      {
      analogWrite(pins[i], brightness);
      delay(1);
      }
    delay(40);
    }
    for (int i=0; i<=5; i++)
    {
    for (int brightness = 255; brightness >= 0; brightness--)
      {
      analogWrite(pins[i], brightness);
      delay(1);
      }
    delay(40);
    }
  }
 
  if(enable == false)
  {
  for(int i = 0; i <= 5; i++) digitalWrite(pins[i], LOW);
  }
 
  lastButton = currentButton;
}

Визуально скетч стал несколько сложнее. На самом деле здесь все просто и давайте в этом разберемся. Нам необходимо идентифицировать все подключенные светодиоды, но вместо привычного int led мы используем массив, каждый элемент которого является PWM-контактом на Arduino. В теле функции void setup() мы тоже поступили хитрым образом. "Перечислять" все контакты мы доверили циклу for(), с каждой итерацией которого производится конфигурация соответствующего контакта на OUTPUT. Переходим к функции void loop(). Функция debounce() и начальное условие if() остается без изменений. У нас по-прежнему идет проверка уровней двух переменных: предыдущее значение (изначально LOW) и текущее состояние кнопки. При выполнении этих условий значение переменной enable инвертируется. Учитывая это, мы добавили еще два простых условия if(). Если enable = true, то гирлянда включается, плавностью "перетекания" которой управляет цикл for(). Если же enable = false, то все светодиоды выключены. По окончанию условий переменная lastButton принимает текущее состояние кнопки.
Тестируя нашу программу, мы заметили, что все работает не должным образом. Помните, в прошлом уроке мы сделали поправку, что при большом значении временной задержки кнопка срабатывает по её истечению? В прошлом примере, при включенной гирлянде, суммарная задержка в теле функции void loop() составляла 85мс. Это давало нам возможность успеть "попасть" в определенной отрезок времени. В данном скетче, при том же условии, задержка отличается в несколько раз. Возможно, при желании выключить гирлянду напрашивается слово "прервать". Это и будет являться решением данной задачи!

Надеюсь, что эта статья была для Вас полезной. В следующем уроке мы рассмотрим прерывания в Arduino и добьемся должного результата.

2 коммент.:

Дмитрий комментирует...

Замечательная, среди остальных, статья о ШИМ в Arduino! Спасибо, все работает.

Виталий комментирует...

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

Отправить комментарий

 
Copyright © 2013 | Arduinokit
Arduino проекты. Уроки, программирование, управление и подключение ардуино.