Освой Arduino играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Работаем с множеством светодиодов

05.Control: ForLoopIteration
05.Control: Arrays
Бегущие огни
07.Display: barGraph (Световая шкала и потенциометр)

Мигать одним светодиодом не слишком интересно. В этом уроке мы рассмотрим работу с множеством светодиодов. Если проявить фантазию, то можно создавать интересные эффекты.

Сам принцип работы со светодиодами не меняется, мы также задаём номера выводом и подаём нужные сигналы. Но проблема заключается в том, что придётся писать однотипный код для каждого светодиода. И когда светодиодов наберётся большое количество и вы решите поменять логику, то придётся искать и менять код у каждого светодиода. Это не очень удобно. Поэтому для облегчения рутинной работы используют циклы, массивы, коллекции.

05.Control: ForLoopIteration

Для знакомства с циклом for в Arduino IDE есть пример File | Examples | 05.Control | ForLoopIteration.

Для эксперимента нам понадобятся шесть светодиодов. Соответственно, к ним нужно добавить шесть резисторов. Соединяем их как на рисунке. Задействуем цифровые выводы 2, 3, 4, 5, 6, 7.

LEDs

Цель скетча - поочерёдно зажигать и гасить светодиоды в одном направлении, а затем в другом.


int timer = 100;           // интервал между миганиями светодиодов

void setup() {
  // проходимся в цикле по каждому светодиоду от 2 до 7 и влючаем нужный режим
  for (int thisPin = 2; thisPin < 8; thisPin++) {
    pinMode(thisPin, OUTPUT);
  }
}

void loop() {
  // опять проходимся в цикле по каждому светодиоду
  for (int thisPin = 2; thisPin < 8; thisPin++) {
    // включаем
    digitalWrite(thisPin, HIGH);
    delay(timer);
    // выключаем
    digitalWrite(thisPin, LOW);
  }

  // ещё раз проходимся в цикле, но в обратном порядке от 7 до 2
  for (int thisPin = 7; thisPin >= 2; thisPin--) {
    // включаем
    digitalWrite(thisPin, HIGH);
    delay(timer);
    // выключаем
    digitalWrite(thisPin, LOW);
  }
}

Доказательство, что код работает.

05.Control: Arrays

Обращаться к каждому светодиоду можно не только по очереди в цикле, но и через массив. Использование массивов даёт больше гибкости. Посмотрим на примере File | Examples | 5.Control | Arrays. Схема остаётся прежней из предыдущего примера.

Массив объявляется с помощью квадратных скобок, а затем к переменной массива обращаются, указывая в квадратных скобках индекс массива, который начинается с 0. Таким образом, чтобы обратиться к первому элементу массива, следует писать ledPins[0] и т.д. Комментарии к скетчу смотрите в предыдущем примере.


int timer = 100;
int ledPins[] = {
  2, 7, 4, 6, 5, 3
};       // массив в случайном порядке
int pinCount = 6;           // количество светодиодов (размер массива)

void setup() {
  for (int thisPin = 0; thisPin < pinCount; thisPin++) {
    pinMode(ledPins[thisPin], OUTPUT);
  }
}

void loop() {
  for (int thisPin = 0; thisPin < pinCount; thisPin++) {
    digitalWrite(ledPins[thisPin], HIGH);
    delay(timer);
    digitalWrite(ledPins[thisPin], LOW);

  }

  // loop from the highest pin to the lowest:
  for (int thisPin = pinCount - 1; thisPin >= 0; thisPin--) {
    digitalWrite(ledPins[thisPin], HIGH);
    delay(timer);
    digitalWrite(ledPins[thisPin], LOW);
  }
}

Если вы замените строку int ledPins[] = {2, 7, 4, 6, 5, 3}; на int ledPins[] = {2, 3, 4, 5, 6, 7};, то получите точно такое же поведение светодиодов из предыдущего примера с циклом for, когда светодиоды загораются и гаснут по очереди. Но использование массива позволяет поменять начальное положение светодиодов, не меняя остальной код. И вы можете только в одном месте менять начальные позиции для запуска волны. Например, зададим массив через одного: {2, 4, 6, 3, 5, 7}.

Бегущие огни

Ещё один вариант бегущих по порядку огней. На этот раз уместим код в один цикл for, добавив переменную, следящую за направлением движения.


const int ARRAY_SIZE = 6;

int ledPin[] = {2, 3, 4, 5, 6, 7};
int ledDelay = 500;
int direction = 1;
int currentLed = 0;
unsigned long changeTime;

void setup() {
  for (int i = 0; i < ARRAY_SIZE; i++) {
    pinMode(ledPin[i], OUTPUT);
  }
  changeTime = millis();

}

void loop() {
  if ((millis() - changeTime) > ledDelay) {
    changeLed();
    changeTime = millis();
  }
}

void changeLed() {
  // выключаем все светодиоды
  for (int i = 0; i < ARRAY_SIZE; i++) {
    digitalWrite(ledPin[i], LOW);
  }
  // включаем текущий LED
  digitalWrite(ledPin[currentLed], HIGH);
  // увеличиваем значение
  currentLed += direction;
  // меняем направление, если достигли конца
  if (currentLed == ARRAY_SIZE - 1) {
    direction = -1;
  }
  if (currentLed == 0) {
    direction = 1;
  }
}

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

07.Display: barGraph (Световая шкала и потенциометр)

Рассмотрим пример с использованием светодиодной шкалы и потенциометра - Examples | 07.Display | barGraph. Если световой шкалы нет, то замените на 10 обычных светодиодов.

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

Добавим на схему потенциометр. Средняя ножка ведёт на аналоговый вывод A0, а остальные две на 5 V и GND.

Управление световой шкалой


// константы
const int analogPin = A0;   // порт для потенциометра
const int ledCount = 10;    // число светодиодов на светодиодной шкале

int ledPins[] = { 
  2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };   // массив портов, к которым привязаны светодиоды

void setup() {
  // проходим через все элементы массива и устанавливаем режим для вывода
  for (int thisLed = 0; thisLed < ledCount; thisLed++) {
    pinMode(ledPins[thisLed], OUTPUT); 
  }
}

void loop() {
  // считываем сигнал с потенциометра
  int sensorReading = analogRead(analogPin);
  // трансформируем результат в диапазон от 0 до 10 (по числу светодиодов)
  int ledLevel = map(sensorReading, 0, 1023, 0, ledCount);

  // проходим через массив светодиодов
  for (int thisLed = 0; thisLed < ledCount; thisLed++) {
    // если индекс элемента массива меньше чем ledLevel,
    // включаем порт для данного элемента:
    if (thisLed < ledLevel) {
      digitalWrite(ledPins[thisLed], HIGH);
    } 
    // Выключаем все порты, которые выше чем ledLevel:
    else {
      digitalWrite(ledPins[thisLed], LOW); 
    }
  }
}

Данный пример интересен функцией map(), предназначенной для пропорционального перевода значений одного диапазона в значения другого диапазона. Мы знаем, что потенциометр может выводить результаты от 0 до 1023, а у нас всего десять светодиодов. Функция нам и поможет в преобразовании.


int ledLevel = map(sensorReading, 0, 1023, 0, ledCount);

Все значения будут равномерно распределены от 0 до 10 (приблизительно 102 единицы потенциометра на одну единицу). Представим себе, что у нас потенциометр показывает значение 110 единиц, что соответствует значению 1 после применения функции. Первый светодиод в массиве имеет значение 0, т.е. меньше 1. Первый светодиод загорится, а остальные погаснут (если горели до этого). Поворачивая ручку потенциометра, мы увеличиваем значения и соответственно увеличиваем число включённых светодиодов. Поворачивая ручку потенциометра в обратную сторону, мы уменьшаем число включённых светодиодов. Чтобы следить за результатами, добавьте в код наблюдение за последовательным портом Serial


  int ledLevel = map(sensorReading, 0, 1023, 0, ledCount);
  Serial.println(sensorReading);
  delay(1);
  ...

Демонстрация результата.

Реклама