Главная chevron_right Функции chevron_right Математика chevron_right map()

map()

map() линейно пересчитывает число из одного диапазона в другой — например, показания analogRead() (0–1023) в диапазон analogWrite() (0–255).

Полная статья

Описание

map() выполняет линейное масштабирование: значение fromLow отображается в toLow, значение fromHigh — в toHigh, а все промежуточные значения пересчитываются пропорционально.

Это удобно, когда нужно привести данные одного диапазона к шкале другого — например, перевести показания АЦП (0–1023) в диапазон ШИМ (0–255) или в градусы поворота сервопривода.

Функция не ограничивает результат границами целевого диапазона: если value выходит за пределы исходного диапазона, результат тоже выйдет за пределы целевого. Это сделано намеренно — иногда именно такое поведение нужно. Если же вы хотите жёстко удержать результат в границах, примените constrain() до или после вызова map().

Границы диапазонов могут быть заданы в любом порядке: «нижняя» граница не обязана быть меньше «верхней». Это позволяет инвертировать диапазон. Например, следующий вызов разворачивает шкалу:

y = map(x, 1, 50, 50, 1);

Функция корректно работает с отрицательными числами:

y = map(x, 1, 50, 50, -100);

Важно понимать, что map() использует целочисленную арифметику. Дробная часть результата просто отбрасывается — без округления. Если математически должно получиться 1.7, функция вернёт 1. Для проектов, где важна точность (например, пересчёт напряжения с несколькими знаками после запятой), лучше реализовать формулу вручную с использованием float.

Синтаксис

map(value, fromLow, fromHigh, toLow, toHigh)

Параметры

  • value — число, которое нужно пересчитать.
  • fromLow — нижняя граница исходного диапазона.
  • fromHigh — верхняя граница исходного диапазона.
  • toLow — нижняя граница целевого диапазона.
  • toHigh — верхняя граница целевого диапазона.

Все параметры и возвращаемое значение имеют тип long, поэтому передавать дробные числа не имеет смысла — они будут усечены ещё до вычислений.

Возвращает

Пересчитанное значение. Тип данных: long.

Пример кода

Следующий скетч читает аналоговый сигнал (0–1023) с вывода A0 и масштабирует его в диапазон 0–255 для управления яркостью светодиода через ШИМ на выводе 9. Оба значения выводятся в Serial Monitor.

/* Преобразование аналогового значения в 8 бит (от 0 до 255) */
void setup() {
  Serial.begin(9600);
}

void loop() {
  int val = analogRead(0);

  Serial.print("Input value: ");
  Serial.print(val);

  val = map(val, 0, 1023, 0, 255); // функция преобразования диапазона

  Serial.print(", Output value: ");
  Serial.println(val);

  analogWrite(9, val); // вывод PWM
  delay(200);
}

Если подключить потенциометр к аналоговому выводу (A0), а светодиод — к выводу 9, поворот ручки потенциометра будет плавно менять яркость светодиода.

Формула

Для тех, кто хочет понять математику:

long map(long x, long in_min, long in_max, long out_min, long out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

Где применяется

  • Управление сервоприводом — перевод угла поворота (например, 0–180°) в значение для writeMicroseconds() (1000–2000 мкс).
  • Масштабирование АЦП — приведение показаний analogRead() (0–1023) к диапазону analogWrite() (0–255).
  • Инверсия сигнала — когда нужно, чтобы максимальное входное значение давало минимальный выход, и наоборот.
  • Пересчёт показаний датчика — например, перевод сырых данных датчика температуры в градусы Цельсия по известной линейной характеристике.

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

  • map() не блокирует выполнение программы — это просто математическая функция, результат возвращается мгновенно.
  • Если value выходит за пределы fromLowfromHigh, результат тоже выйдет за пределы toLowtoHigh. Чтобы этого избежать, оберните вызов в constrain().
  • Не передавайте одинаковые значения в fromLow и fromHigh: в формуле появится деление на ноль.
  • Целочисленное усечение заметно при маленьких диапазонах. Например, при масштабировании 0–5 в 0–3 несколько входных значений будут давать одинаковый результат — это нормально, но стоит учитывать при проектировании интерфейса.
  • Дроби вида 3/2, 4/3, 5/4 возвращаются как 1 — это не ошибка, а следствие целочисленной арифметики.
  • Если точность важна, вычислите результат вручную через float: float result = (float)(value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow;
  • При отладке выводите в Serial Monitor одновременно исходное и пересчитанное значение — это быстро покажет, правильно ли заданы границы диапазонов.

Полезные материалы