Скетч для первого поворота я не писал — честно взял пример из IDE: File, Examples, Stepper, stepper_oneRevolution, поменял пины под свою распайку и залил (как заливают скетч). Мотор совершил какое-то мелкое движение и замер. Я ждал оборот, а вал проехал градусов тридцать пять и остановился. Хорошая новость: железо было исправно. Плохая: пример писали под моторы с двумястами шагами на оборот, а у моей 28BYJ-48 их две тысячи сорок восемь.
Библиотека Stepper не знает и не может знать, какой мотор к ней подключён. Единственный источник правды — число, которое вы кладёте в конструктор. Скажете 200 — каждый ваш «оборот» окажется 35,2 градуса вала; скажете 2048 — получите честные 360. Никакой магии, чистая пропорция, но именно на ней спотыкается каждый второй первый скетч: примеры в интернете писались под крупные моторы, а наборы в коробках лежат почти всегда с 28BYJ-48.
В этом уроке разберём три вызова, из которых состоит любой скетч со Stepper: конструктор, setSpeed и step. Посмотрим, что принимает каждый аргумент, где в примерах из интернета спрятаны ловушки, и запустим мотор в симуляторе с циферблатом — там каждый шаг виден глазами, а Serial покажет, сколько времени программа провела взаперти.
Три вызова: конструктор, setSpeed, step
Конструктор выглядит так: Stepper имя(шагов на оборот, пин, пин, пин, пин). Первый аргумент — те самые 2048 для 28BYJ-48; дальше четыре пина, к которым через ULN2003 идут сигнальные линии IN1-IN4. Порядок не по возрастанию — 8, 10, 9, 11: библиотека гонит фазы в своей внутренней последовательности, и пины должны ей соответствовать. Откуда взялся этот рецепт и почему перепутанные линии дают гул вместо вращения — разобрано в уроке про подключение 28BYJ-48 и ULN2003.
Существует и двухаргументный вариант конструктора — для биполярных моторов с драйвером-мостом: сначала два пина, потом число шагов. Я столкнулся с ним, когда заказал NEMA 17, и первое время путал варианты местами. Правило простое: смотрите на число аргументов в чужом скетче, прежде чем переносить строку к себе — четырёхпиновая запись и двухпиновая не взаимозаменяемы.
Второй обязательный вызов — setSpeed. Документация библиотеки прямо просит вызывать его до первого step: из оборотов в минуту считается задержка между переключениями фаз. Я однажды выкинул setSpeed при чистке скетча от лишнего — и мотор превратился в зуммер: фазы мелькали без паузы, ротор стоял. Задержка внутри библиотеки считалась от нуля, и двигателю попросту не хватало времени наехать на следующий шаг.
| Вызов | Что принимает | Что делает | Типовая ошибка |
|---|---|---|---|
| Stepper(2048, 8, 10, 9, 11) | Шагов на оборот вала и четыре пина | Создаёт объект, запоминает порядок фаз | Оставить 200 из примера IDE |
| setSpeed(5) | Оборотов в минуту (RPM) | Считает задержку между тактами фаз | Вызвать после первого step или забыть совсем |
| step(2048) | Число шагов, минус — назад | Гонит фазы, пока не досчитает все | Ждать, что управление вернётся сразу |
Теперь о главном свойстве step — блокировке. Внутри функции цикл: включить нужные фазы, выждать рассчитанные миллисекунды, перейти к следующему такту — и так до последнего шага. Пока цикл крутится, loop() стоит: Serial молчит, кнопки не опрашиваются, таймеры не проверяются. При 2048 шагах на оборот и 5 RPM один шаг занимает 5,9 миллисекунды, оборот — ровно 12 секунд; при 15 RPM — 1,95 мс на шаг и 4 секунды на оборот. Всё это время программа заперта внутри одного вызова. Арифметика тут прозрачная: 60 секунд делим на обороты в минуту и на число шагов — и получаем паузу между тактами; библиотека делает ровно это в setSpeed.
Отсюда два рабочих правила. Первое: скорость выбирайте по задаче, а не по красоте цифры — про потолок 28BYJ-48 и срыв шагов у нас есть отдельный разговор, выше 15 RPM этот мотор начинает гудеть на месте. Второе: длинные перемещения дробите на порции, если в loop() живут кнопки или датчики, — приём с millis() разобран там же.
Дробных шагов у библиотеки нет: step(1024.5) она молча обрежет до 1024. Отрицательное число — вращение назад, step(0) — честный ноль работы. Всё остальное — арифметика, которую вы обязаны посчитать сами и передать готовым числом; именно на этом пересчёте спотыкаются чаще всего, и третьему уроку раздела он и посвящён.
#include <Stepper.h> const int STEPS_PER_REV = 2048; // полных шагов на оборот вала Stepper stepper(STEPS_PER_REV, 8, 10, 9, 11); void setup() { stepper.setSpeed(5); // до первого step() — обязательно Serial.begin(9600);} void loop() { unsigned long t0 = millis(); stepper.step(1024); // пол-оборота вперёд Serial.print("Pol oborota za ms="); Serial.println(millis() - t0); delay(1000); stepper.step(-1024); // и назад delay(1000);}В скетче три строки, на которые стоит смотреть отдельно. Строка с #include нужна только настоящей Arduino IDE: она подключает заголовок библиотеки, установленной через Library Manager — как ставить библиотеки мы проходили в самом начале. Если IDE пишет No such file or directory, значит, Stepper просто не установлена. В симуляторе заголовки не нужны, поэтому в его скетчах ниже вы их не увидите.
Пол-оборота — это 1024 шага. При setSpeed(5) библиотека отводит на шаг 5,86 мс, и step(1024) вернёт управление секунд через шесть: Serial напечатает разницу millis() около 6000 с небольшим хвостиком. Хвостик — это накладные расходы на печать и loop; на настоящей плате он вырастет до десятков миллисекунд, если Serial висит на 9600 бод, потому что каждый символ там стоит около миллисекунды эфира. Для сравнения времени с расчётом ставьте Serial.begin до первого step — иначе первые строки уйдут в никуда.
Третья строка — порядок пинов 8, 10, 9, 11. Перепутаете 9 и 10 — мотор не откажется работать, он будет дёргаться и гудеть: фазы пойдут вразнобой, и ротору некуда ехать. Диагностика одна и та же на все случаи: светодиоды на плате ULN2003 обязаны мигать по кругу. Наблюдаете круг — механика и порядок в норме, ищите причину дальше по цепочке. Порядок обхода можно и проверить программно: временно замените step() на четыре digitalWrite по кругу с паузой в полсекунды — если стрелка симулятора поползла, а светодиоды перекликаются по одному, порядок верный.
Проверяем в симуляторе
В симуляторе мотор уже сидит на пинах 8, 10, 9, 11 за ULN2003, а рядом с платой — циферблат: стрелка повторяет угол вала. Внутри step() блокирует loop() точно так же, как на настоящей плате, поэтому разница millis() в Serial — честная, её можно сравнивать с расчётом на бумажке. Смотреть на показания удобно в Serial-мониторе: строки идут парами, время шага напечатано числом. Единственное отличие от реального железа — симулятор не умеет срывать шаги: он честно досчитывает любые команды, поэтому диагностику скорости он не заменяет, а вот арифметику времени показывает безупречно.
Задача на прогон: убедиться, что четверть оборота (512 шагов) при 6 RPM занимает 2,5 секунды, и что всё это время программа не отвечает. Потом поднимите скорость и сравните числа — зависимость времени от RPM линейная, и вы это увидите в цифрах, а не в ощущениях.
Отдельно посмотрите на паузу между строками Serial: она равна времени внутри step(). Пока стрелка стоит, loop() мёртв — ни кнопка, ни датчик в этот момент не опросятся. Именно поэтому в уроке про позиционирование мы дробили длинные ходы на порции; сейчас же наша цель — прочувствовать цену одной целиковой команды. Скажем прямо: для заслонки этого достаточно, а для кнопочного интерфейса — уже нет.
- Поверните вал на 90 градусов: 512 шагов, при 6 RPM это 2,5 секунды. Сверьте время из Serial с расчётом.
- Заведите функцию quarter() с шагом step(512) внутри — свои функции в скетче сокращают код, — и вызовите её четыре раза подряд: должен выйти полный оборот за 10 секунд при 6 RPM.
- Поднимите setSpeed до 30. В симуляторе стрелка честно доедет — он не умеет срывать шаги. На настоящей плате 30 RPM для 28BYJ-48 означает гудение на месте: найдите предел своего экземпляра экспериментом.
- Первый аргумент конструктора — шаги на оборот вала: 2048 для 28BYJ-48; 200 из примеров IDE дают поворот на 35°
- setSpeed(RPM) вызывайте до первого step() — из неё считается задержка между фазами; без неё фазы мелькают без паузы и мотор зудит
- step(N) блокирует loop(): при 5 RPM шаг — 5,9 мс, оборот — 12 секунд
- Минус в step() — вращение назад; дробные шаги молча обрезаются до целых
- Порядок пинов 8, 10, 9, 11 не переставлять: перепутанные фазы дают гул вместо вращения
- 1. Сколько шагов на оборот нужно указать в конструкторе для 28BYJ-48?
- 2. Что задаёт вызов setSpeed(10)?
- 3. Почему пример stepper_oneRevolution повернул 28BYJ-48 лишь на 35 градусов?
- 4. Сколько времени займёт step(2048) при setSpeed(10)?
- 5. Когда вернётся управление после вызова step(1024) при setSpeed(5)?
Библиотека освоена: конструктор, скорость, шаги — мотор слушается арифметики. Следующий шаг — заглянуть под капот: что именно библиотека делает с обмотками в эти 5,9 миллисекунды, почему существуют полный и полушаговый режимы и как они меняют плавность и момент. Второй урок раздела вскрывает последовательность фаз — там будет скетч, который гонит обмотки вручную, без Stepper.h вовсе.
Частые вопросы по теме урока
- Сколько шагов на оборот нужно указать в конструкторе для 28BYJ-48?
- 32 шага ротора умножаем на передаточное число редуктора: 32 × 64 = 2048. Двести — число из примеров IDE под моторы NEMA-класса. 2048
- Что задаёт вызов setSpeed(10)?
- Аргумент setSpeed — обороты в минуту (RPM). Из них библиотека сама считает паузу между переключениями обмоток: 60 секунд делит на RPM и на 2048 шагов. Десять оборотов в минуту
- Почему пример stepper_oneRevolution повернул 28BYJ-48 лишь на 35 градусов?
- Пример писали под моторы с 200 шагами на оборот. 200 шагов из 2048 — это 35,2 градуса. Число в конструкторе должно быть честным. В примере стоит 200 шагов на оборот вместо 2048
- Сколько времени займёт step(2048) при setSpeed(10)?
- 10 RPM — оборот за 60 секунд, и всё это время step() держит loop() взаперти, пока не досчитает все шаги. 60 секунд
- Когда вернётся управление после вызова step(1024) при setSpeed(5)?
- step() блокирующий: при 5 RPM шаг занимает 5,86 мс, на 1024 шага уходит около 6 секунд. Только потом выполняется следующая строка. Через 6 секунд, когда вал доедет