Главная chevron_right Уроки chevron_right Электроника chevron_right Основы работы с потенциометром и Arduino

Основы работы с потенциометром и Arduino

Разбираемся, как устроен потенциометр, в каких форматах он встречается и как подключить его к Arduino для управления цветом светодиодов.

Потенциометр — это простое механическое устройство, которое существует во множестве форм. Его суть — переменное сопротивление, которое меняется при физическом воздействии. В примерах этой статьи используется потенциометр с поворотной ручкой — один из самых распространённых вариантов.

Подавая напряжение через потенциометр на аналоговый вход Arduino, можно считывать текущее положение ручки как аналоговое значение. Статья покажет два практических сценария: потенциометр как вход для смешивания цветов RGB, а также плавное перекрёстное затухание между цветами.

Типичный потенциометр имеет 3 вывода: два для питания (+5V и GND) и один сигнальный, который подключается к аналоговому входу Arduino.

Чтобы узнать, как считывать данные с потенциометра и выводить их в монитор Serial, посетите Analog Read Serial example.

Необходимые компоненты

  • Arduino board
  • Потенциометр
  • 1x красный, 1x зелёный, 1x синий светодиод (LED)
  • 3x резистора 220 Ом

Схема подключения

  • Плюсовой вывод потенциометра → 5V
  • Минусовой вывод потенциометра → GND
  • Сигнальный вывод потенциометра → A3
  • Красный LED → пин 9 через резистор 220 Ом
  • Зелёный LED → пин 10 через резистор 220 Ом
  • Синий LED → пин 11 через резистор 220 Ом

Complete Circuit

Подсказка по схеме. Обратите внимание на полярность светодиодов: длинная ножка (анод) идёт к пину Arduino через резистор, короткая (катод) — к GND. Если светодиод не светится, скорее всего, он вставлен наоборот.

Как это работает

Потенциометр делит входное напряжение (5V) пропорционально положению ручки. Когда ручка повёрнута до упора в одну сторону — на сигнальном выводе почти 0V, в другую — почти 5V. Функция analogRead() преобразует это напряжение в целое число от 0 до 1023. Затем функция map() или простая математика позволяет перевести это значение в нужный диапазон — например, 0–255 для управления яркостью через analogWrite().

Пример: смеситель цветов

Этот пример показывает, как потенциометр используется в качестве аналогового входа для тонкого управления смешиванием цветов RGB.

/*
* Код для управления тремя светодиодами (красным, зелёным и синим) или одним RGB-светодиодом с помощью одного потенциометра.
* Программа плавно переходит от красного к зелёному, от зелёного к синему и от синего к красному.
* Clay Shirky <clay.shirky@nyu.edu> 
*/

// ВХОД: потенциометр подключить к 5V и GND
int potPin = A3; // Выход потенциометра подключён к аналоговому пину 3
int potVal = 0; // Переменная для хранения значения с потенциометра

// ВЫХОД: используются цифровые пины 9–11 (PWM)
// Катоды светодиодов подключить к цифровому GND
int redPin = 9;   // Красный светодиод подключён к цифровому пину 9
int grnPin = 10;  // Зелёный светодиод подключён к цифровому пину 10
int bluPin = 11;  // Синий светодиод подключён к цифровому пину 11

// Переменные программы
int redVal = 0;   // Переменные для хранения значений, передаваемых на пины
int grnVal = 0;
int bluVal = 0;

void setup()
{
  pinMode(redPin, OUTPUT);   // Настройка пинов как выходов
  pinMode(grnPin, OUTPUT);   
  pinMode(bluPin, OUTPUT); 
}

// Основная программа
void loop()
{
  potVal = analogRead(potPin);   // Считать значение потенциометра с входного пина

  if (potVal < 341)  // Нижняя треть диапазона потенциометра (0–340)
  {                  
    potVal = (potVal * 3) / 4; // Нормализация до 0–255

    redVal = 256 - potVal;  // Красный: от максимума до нуля
    grnVal = potVal;        // Зелёный: от нуля до максимума
    bluVal = 1;             // Синий: выключен
  }
  else if (potVal < 682) // Средняя треть диапазона потенциометра (341–681)
  {
    potVal = ( (potVal-341) * 3) / 4; // Нормализация до 0–255

    redVal = 1;            // Красный: выключен
    grnVal = 256 - potVal; // Зелёный: от максимума до нуля
    bluVal = potVal;       // Синий: от нуля до максимума
  }
  else  // Верхняя треть диапазона потенциометра (682–1023)
  {
    potVal = ( (potVal-683) * 3) / 4; // Нормализация до 0–255

    redVal = potVal;       // Красный: от нуля до максимума
    grnVal = 1;            // Зелёный: выключен
    bluVal = 256 - potVal; // Синий: от максимума до нуля
  }
  analogWrite(redPin, redVal);   // Записать значения на пины светодиодов
  analogWrite(grnPin, grnVal); 
  analogWrite(bluPin, bluVal);  
}

Пример: плавное перекрёстное затухание цветов

/*
* Код для плавного перехода между тремя цветами: красным, зелёным и синим (RGB).
* Для создания переходов нужно сделать две вещи:
*  1. Описать цвета, которые должны отображаться.
*  2. Задать порядок, в котором они сменяют друг друга.
*
* ОПИСАНИЕ ЦВЕТА:
* Цвет — это массив из трёх значений в процентах (0–100),
*  управляющих красным, зелёным и синим светодиодами.
*
* Красный — красный светодиод на максимуме, синий и зелёный выключены:
*   int red = { 100, 0, 0 }
* Тусклый белый — все три светодиода на 30%:
*   int dimWhite = {30, 30, 30}
* и т.д.
*
* Ниже приведены распространённые цвета, можно добавить свои.
*
* ПОРЯДОК СМЕНЫ ЦВЕТОВ:
* В основной части программы нужно указать порядок смены цветов, например:
*  crossFade(red);
*  crossFade(green);
*  crossFade(blue);
*
* Цвета будут сменяться в указанном порядке с плавным переходом
*    от одного к другому.
*
* Дополнительно можно настроить 5 параметров:
* 1. Начальный цвет по умолчанию — чёрный (первый цвет плавно появляется),
*    но можно задать любой другой начальный цвет.
* 2. Внутренний цикл выполняется 1020 итераций; переменная 'wait'
*    задаёт примерную длительность одного перехода. Теоретически
*    'wait' = 10 мс даёт переход ~10 секунд. На практике
*    из-за других операций это занимает ~11 секунд на моей плате. YMMV.
* 3. Если 'repeat' = 0, программа работает бесконечно.
*    Если задано число, цикл повторится указанное количество раз,
*    затем остановится на последнем цвете. (Установите 'return' = 1
*    и сделайте последний цвет чёрным, если нужно плавное угасание в конце.)
* 4. Есть необязательная переменная 'hold' — пауза в миллисекундах
*    после завершения цвета, перед началом следующего перехода.
* 5. Установите флаг DEBUG = 1, чтобы выводить отладочную информацию
*    в монитор порта.
*
*    Внутренняя логика программы несложная, но требует аккуратности —
*    подробное объяснение приведено ниже основного цикла.
*
* Апрель 2007, Clay Shirky <clay.shirky@nyu.edu> 
*/ 

// Выход
int redPin = 9;   // Красный светодиод подключён к цифровому пину 9
int grnPin = 10;  // Зелёный светодиод подключён к цифровому пину 10
int bluPin = 11;  // Синий светодиод подключён к цифровому пину 11

// Массивы цветов
int black[3]  = { 0, 0, 0 };
int white[3]  = { 100, 100, 100 };
int red[3]    = { 100, 0, 0 };
int green[3]  = { 0, 100, 0 };
int blue[3]   = { 0, 0, 100 };
int yellow[3] = { 40, 95, 0 };
int dimWhite[3] = { 30, 30, 30 };
// и т.д.

// Задать начальный цвет
int redVal = black[0];
int grnVal = black[1]; 
int bluVal = black[2];

int wait = 10;      // Внутренняя задержка crossFade 10 мс; увеличьте для более медленных переходов
int hold = 0;       // Необязательная пауза после завершения цвета, перед следующим crossFade
int DEBUG = 1;      // Счётчик DEBUG; если = 1, значения передаются через serial
int loopCount = 60; // Как часто выводить отладочную информацию?
int repeat = 3;     // Сколько раз повторять цикл перед остановкой? (0 — без остановки)
int j = 0;          // Счётчик повторений

// Инициализация переменных цвета
int prevR = redVal;
int prevG = grnVal;
int prevB = bluVal;

// Настройка выходов для светодиодов
void setup()
{
  pinMode(redPin, OUTPUT);   // Настройка пинов как выходов
  pinMode(grnPin, OUTPUT);   
  pinMode(bluPin, OUTPUT); 

  if (DEBUG) {           // Если нужен вывод отладочных значений...
    Serial.begin(9600);  // ...инициализировать serial 
  }
}

// Основная программа: задать порядок переходов crossFade
void loop()
{
  crossFade(red);
  crossFade(green);
  crossFade(blue);
  crossFade(yellow);

  if (repeat) { // Нужно ли ограничить число повторений?
    j += 1;
    if (j >= repeat) { // Достигли нужного числа?
      exit(j);         // Если да — остановиться.
    }
  }
}

/* НИЖЕ — МАТЕМАТИКА. ДЛЯ БАЗОВОГО ИСПОЛЬЗОВАНИЯ МЕНЯТЬ НЕ НУЖНО.
*
* Принцип работы программы:
* Представьте переход, в котором красный LED меняется с 0 до 10,
*   зелёный — с 0 до 5, а синий — с 10 до 7, за десять шагов.
*   Нужно отсчитать 10 шагов и равномерно увеличивать или
*   уменьшать значения цветов.
*   Пусть + означает увеличение на 1, а - — уменьшение.
*   Тогда 10-шаговый переход выглядит так:
*
*   1 2 3 4 5 6 7 8 9 10
* R + + + + + + + + + +
* G   +   +   +   +   +
* B     -     -     -
*
* Красный растёт с 0 до 10 за десять шагов, зелёный —
* с 0 до 5 за пять шагов, синий падает с 10 до 7 за три шага.
*
* В реальной программе проценты цвета переводятся в значения 0–255,
* а шагов всего 1020 (255*4).
*
* Чтобы определить размер шага между изменениями значения LED,
* вызывается calculateStep() — она вычисляет абсолютную разницу
* между начальным и конечным значениями, затем делит её на 1020.
*/

int calculateStep(int prevValue, int endValue) {
  int step = endValue - prevValue; // Какова общая разница?
  if (step) {                      // Если не ноль, 
    step = 1020/step;              //   делим на 1020
  } 
  return step;
}

/* Следующая функция — calculateVal. Когда счётчик цикла i
*  достигает размера шага для одного из цветов,
*  она увеличивает или уменьшает значение этого цвета на 1.
*  (R, G и B вычисляются независимо.)
*/

int calculateVal(int step, int val, int i) {

  if ((step) && i % step == 0) { // Если шаг ненулевой и пришло время изменить значение,
    if (step > 0) {              //   увеличить значение, если шаг положительный...
      val += 1;           
    } 
    else if (step < 0) {         //   ...или уменьшить, если шаг отрицательный
      val -= 1;
    } 
  }
  // Защита: убедиться, что val остаётся в диапазоне 0–255
  if (val > 255) {
    val = 255;
  } 
  else if (val < 0) {
    val = 0;
  }
  return val;
}

/* crossFade() переводит процентные значения цветов в диапазон 0–255,
*  затем выполняет 1020 итераций, проверяя необходимость обновления
*  значений и записывая их на соответствующие пины.
*/

void crossFade(int color[3]) {
  // Перевести в диапазон 0–255
  int R = (color[0] * 255) / 100;
  int G = (color[1] * 255) / 100;
  int B = (color[2] * 255) / 100;

  int stepR = calculateStep(prevR, R);
  int stepG = calculateStep(prevG, G); 
  int stepB = calculateStep(prevB, B);

  for (int i = 0; i <= 1020; i++) {
    redVal = calculateVal(stepR, redVal, i);
    grnVal = calculateVal(stepG, grnVal, i);
    bluVal = calculateVal(stepB, bluVal, i);

    analogWrite(redPin, redVal);   // Записать текущие значения на пины светодиодов
    analogWrite(grnPin, grnVal);      
    analogWrite(bluPin, bluVal); 

    delay(wait); // Пауза 'wait' миллисекунд перед следующей итерацией

    if (DEBUG) { // Если нужен вывод в serial — печатать в начале 
      if (i == 0 or i % loopCount == 0) { // и каждые loopCount итераций
        Serial.print("Loop/RGB: #");
        Serial.print(i);
        Serial.print(" | ");
        Serial.print(redVal);
        Serial.print(" / ");
        Serial.print(grnVal);
        Serial.print(" / ");  
        Serial.println(bluVal); 
      } 
      DEBUG += 1;
    }
  }
  // Обновить текущие значения для следующей итерации
  prevR = redVal; 
  prevG = grnVal; 
  prevB = bluVal;
  delay(hold); // Необязательная пауза 'wait' миллисекунд перед продолжением цикла
}

Практические замечания

  • Дребезг значений. Аналоговый вход может немного «дрожать» даже при неподвижной ручке. Если это мешает, усредняйте несколько последовательных считываний или используйте небольшой delay().
  • Диапазон analogRead. Функция возвращает значения 0–1023 (10-битный АЦП). Для управления PWM-выходом нужен диапазон 0–255, поэтому используйте map(value, 0, 1023, 0, 255).
  • Три потенциометра вместо одного. Если хочется управлять каждым цветовым каналом независимо, подключите три потенциометра к трём разным аналоговым входам.
  • Выбор резисторов. Резисторы 220 Ом — разумный минимум для защиты светодиодов от тока. Если светодиоды светят слишком ярко или слишком тускло, подберите значение под конкретную модель.

Типичные ошибки

  • Неверный аналоговый пин. Убедитесь, что сигнальный провод потенциометра идёт именно на A3, как указано в схеме — иначе analogRead(A3) будет возвращать случайные значения.
  • Перепутаны питание и GND. Потенциометр симметричен внешне, но если поменять местами +5V и GND, диапазон значений просто инвертируется. Это не сломает устройство, но логика программы будет работать наоборот.
  • Светодиод без резистора. Подключение светодиода напрямую к пину Arduino без резистора почти наверняка выведет из строя либо светодиод, либо пин платы.