Управление драйвером шагового двигателя (step\dir) на прерываниях таймера

Тема в разделе "Микроконтроллеры AVR", создана пользователем InterestedStudent, 8 ноя 2021.

  1. Здраствуйте.
    Не знаю куда опубликовать тему, если ошибся, перенесите в соответствующий раздел.

    Простой пример управления драйвером шагового двигателя (протокол STEP\DIR) на прерываниях таймера.
    Действующие персонажи, драйвер шагового двигателя AM882 Leadshine, шаговый двигатель ST86-114, блок питания 48 вольт, Arduino Mega 2560 (в девичестве ATmega2560).

    Описание драйвера AM882 двигателя на русском
    https://purelogic.ru/docs/elektronika/Leadshine_AM882_user_manual_ru.pdf
    https://darxton.ru/files/pdf/stepperdrive/AM882.pdf

    Описание драйвера английская версия
    http://www.leadshine.com/uploadfile/down/am882m.pdf

    Описание шагового двигателя ST86-114, предположительно этот https://darxton.ru/files/pdf/steppermotor/ST86-114.pdf, его подключение совпало.

    Можно использовать любые драйвера шаговых двигателей которые поддерживают протокол STEP\DIR. Просто внимательно изучайте подключение.

    Подключение
    Подключение Arduino и драйвера, у меня используется схема подключения драйвера с общим катодом.
    Подключение AM882 к Arduino.jpg
    Подключение к Arduino
    Arduino GND -> Драйвер PUL- (PUL- на драйвере объединен с DIR- и ENA-)
    Arduino 46 (L3) -> Драйвер PUL+
    Arduino 45 (L4) -> Драйвер DIR+
    Arduino 44 (L5) -> Драйвер ENA+

    В коде задействован таймер T5, поэтому выбор пал на эти выходы.
    46 (L3) генерирует сигнал Step (он же PUL в описании драйвера), 45 (L4) сигнал DIR, 44 (L5) сигнал ENA.

    Подключение мотора и блока питания к драйверу
    Подключение питания и шагового двигателя к AM882.jpg
    Выводы мотора ST86-114
    Обмотки шагового двигателя.jpg

    Настройка шаговых драйверов
    Настройка идет по току и микро шагу, для драйвера AM882 есть еще дополнительные настройки. Опустим процесс настройки, остановимся лишь на миро шаге, от него зависит настройки таймера.

    Мне нужно получить скорость вращения в 500об.\ минуту. Для моего шагового двигателя один шаг равен 1,8 градуса, следовательно, в одном обороте 200 шагов.

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

    500*200/60=1666 шагов в сек., если микро шаг не установлен. Если используется микро шаг, то полученное значение нужно умножить на кол-во микро шагов в шаге. Для настройки 1/8, полученное значение умножаем на 8. У меня используется микро шаг 1/4.

    В прищепке есть Excel который поможет подобрать предделитель таймера, а так же диапазон доступных скоростей для выбранного предделителя.

    Код
    Код хоть и выполнен в виде класса, это не полноценный класс (и тем более не библиотека), это скорее код упакованный в класс. Сделано это по двум причинам, чтобы скрыть переменные и методы в классе и начать изучение классов.

    Многие части кода взяты у других пользователей во временное пользование.

    Вот краткий список пользователей AlexGyver (alexgyver ru), если нужна отличная библиотека для управления шаговым на ардуине, рекомендую GyverStepper. Pavel Bobkov (chipenable ru), Ilya A. (avrprog.blogspot com).

    Это не готовое решение, скорее мое обучение, но возможно кому-то будет полезно.
     

    Вложения:

    • BitsMacros.h
      Размер файла:
      2,1 КБ
      Просмотров:
      191
    • Def.h
      Размер файла:
      673 байт
      Просмотров:
      159
    • GStypes.h
      Размер файла:
      402 байт
      Просмотров:
      177
    • StepperOnClosing.cpp
      Размер файла:
      13,6 КБ
      Просмотров:
      232
    • StepperOnClosing.h
      Размер файла:
      2 КБ
      Просмотров:
      195
    • StepperOnInterrupt.h
      Размер файла:
      1,2 КБ
      Просмотров:
      166
    • StepperOnInterrupt.cpp
      Размер файла:
      127 байт
      Просмотров:
      183
  2. Файл Расчет таймеров.xlsx, сохранил в zip

    Там все интуитивно понятно
    Расчет таймеров.jpg
     

    Вложения:

  3. parovoZZ

    parovoZZ Гуру

    А зачем в прерываниях?
     
    InterestedStudent нравится это.
  4. Мне нужно было решить следующую задачу:
    Шаговый мотор крутится с заданной скоростью (от 50 до 200 об./минуту) на протяжении заданного времени, от 1 до 30 секунд.
    Данный цикл повторяется несколько сотен раз, при этом у двигателя должна быть максимальная повторяемость по скорости и кол-ву пройденных шагов.

    Решил использовать таймер на прерывания, в режиме PWM (ICR1), чтобы микроконтроллер сам переключал состояние выхода Step. Это позволяет не беспокоится о задержках в основном цикле программы.

    Позже захотелось добавить функционал для других задач, возможность управлением шаговым двигателем с ускорением, торможением, а так же отправки шагового на заданное кол-во шагов, с проверкой в самом прерывании.

    Вся работа шагового двигателя, за исключением ускорений, торможений сделана на прерывании в PWM (ICR1). Ускорение и торможение решил оставить в основном цикле.
     
  5. parovoZZ

    parovoZZ Гуру

    это я понял. В прерывание зачем для этого залезать? Уход в прерывание будет порождать неминуемый джиттер.
     
    InterestedStudent нравится это.
  6. Спасибо, действительно для решения основной задачи прерывания не нужны.

    Вот само прерывание
    Код (C++):
    ISR(TIMER5_OVF_vect)
    {
      _currentPositionDoor += _doorCloses;

      // Устанавливаем скорость мотора
      if (_valueICR5 && _valueICR5 != ICR5)
      {
        ICR5 = _valueICR5;
      }

      // Останавливаем мотор по достижению необходимого положения
      if (ICR5 &&
          _targetPositionDoor == _currentPositionDoor)
      {
        _needStopMovingDoor = true;

        //TODO: Вынести в отдельный Define
        // Запрещаем прерывания
        TIMSK5 = (0 << TOIE5);
        // Отключаем таймер T5
        //TCCR5B |= (0 << CS51);
        TCCR5B |= (0 << CS50);

        _valueICR5 = ICR5 = 0;
      }
    }
    Все, что оно делает, так это считает шаги и проверят текущую позицию с заданной. Но если двигатель работает в режиме постоянных оборотов по времени, то это просто расточительное использование ресурсов контролера.

    Прерывание будет оправдано в режиме движение на заданное кол-во шагов. Так как нужно как-то считать эти шаги, а так же реагировать на их изменение.

    Не уверен, что правильно понимаю значение этого слова. Вы хотите сказать, что такой поток прерываний (с учетом предделителя 1 к 1), может вызвать блокировку другого прерывания или наоборот другое прерывание с более высоким приоритетом вызовет блокировку данного перывания?
     
  7. parovoZZ

    parovoZZ Гуру

    Компаратор может самостоятельно дёргать внешней ногой МК. Для этого прерывания не нужны.
     
  8. Не понял.

    В режиме PWM (ICR1)
    Код (C++):
    // 1110 - PWM (ICR1)
    TCCR5A |= (1 << WGM51) | (0 << WGM50);
    TCCR5B |= (1 << WGM52) | (1 << WGM53);
    С дополнительной настройкой TCCR5A, битов COM5A1 и COM5A0
    Код (C++):
    TCCR5A |= (1 << COM5A1) | (1 << COM5A0);
    Мы получаем работу без прерываний.

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

    Если честно, просто не знаю, что такое джиттер, в контексте прерываний avr. Речь о том, что требуется учитывать вызов прерываний, их приоритет, чтобы понимать, что одно прерывание может заблокировать другое, а так же время выполнение прерывания?
     
  9. Рабочий код без использования прерывания, в прищепке.

    Мое предположение
    полный абсурд, оно будет происходить с той же периодичностью.

    Когда может понадобиться прерывание, прежде всего для подсчета шагов.

    В моей проекте у данного кода есть ограничение, с которым сейчас работаю. При скоростях более 300об/мин., при заданных ускорениях (там жестко зашито 300мм/с), возможен срыв вала. В версии с прерываниями такой проблемы нет, вероятно это связано с периодичностью вызова данной строки.
    Код (C++):
    if (ICR5 != _valueICR5)
    {
        ICR5 = _valueICR5;
    }
    В коде используется таймер на основе micros(), который зависит от выполнения всей программы. Попробую поместить данный код в прерывание на основе другого таймера, который будет вызываться с периодичностью от 1ms до 15ms во время разгона двигателя.
     

    Вложения:

  10. Kakmyc

    Kakmyc Нерд

    AccelStepper.h вполне себе выдает 1200об/мин (ограничение у него 4000шаг/сек), и при этом нормально считает шаги.
    В чем смысл извращения с прерываниями ?
     
  11. Спасибо, будет возможность попробую. Сейчас много работы.

    У меня не было задачи сделать все на прерываниях. Была простая задача, получить всегда одинаковую скорость шагового мотора в конкретном проекте, уже потом было интересно сделать, что-то больше.

    Если использовать библиотеку Gyverstepper и AccelStepper (быстро пробежался по коду) мы получаем зависимость от micros (для Gyverstepper) или millis (для AccelStepper) и задержек в коде. Для Gyverstepper нам нужно всегда вызывать метод tick() в основном цикле loop, а для AccelStepper метод runSpeed(), в этих методах делается шаг шагового двигателя. Если в loop будет задержка, к примеру сработало прерывание Serial, SPI, I2C или еще что-то, то мы получим задержку в формировании следующего шага, и как следствие расхождение скорости.

    В Gyverstepper предлагают вызывать tick по таймеру, но идея вызова тяжелого метода в прерывании, мне не нравится.

    У меня до 6666 шаг/сек, 500об/мин, в режиме микро шага 1/4.
    В таком режиме он работает плавнее.
     
  12. parovoZZ

    parovoZZ Гуру

    Если нужен минимальный джиттер, то программировать необходимо насухую. Без всяких дурин. И вообще, взять совершенно другой МК.
     
  13. Нужно было получить одинаковую скорость, ее контролировал оптический энкодер. Разумеется, чем меньше джиттер, тем лучше результат.

    Вы профессионал, Вы можете решить эту задачу куда более оптимальное. К примеру без библиотек arduino на чистом avr или еще лучше взять какой-нибудь Stm32.

    Решал задачу в рамках своих знаний. Осталось решить вопрос с разгоном, торможением, но до него добраться не могу, нет времени.

    А что касается готовых библиотек, то на мой взгляд Gyverstepper куда более интереснее, чем AccelStepper.
     
  14. Kakmyc

    Kakmyc Нерд

    Зарёкся пользоваться библиотеками в названии которых есть приставка с его именем. Ошибок в коде чуть больше чем дохренища.
    Что же касается AccelStepper'а, не нужно там каждый раз вызывать метод runSpeed. Там есть куча методов позволяющих сделать нужное количество шагов с минимальным разбегом по времени.
     
  15. Спасибо за мнение. Бегло глянул его код и использовал пару вещей, включая ускорение с которым пока не могу разобраться.

    Возможно мы говорим о разных библиотеках, смотрю эту - https://github.com/adafruit/AccelStepper/blob/master/AccelStepper.cpp, там все методы так или иначе идут в runSpeed().

    Думаю, что есть всего два варианта или таймер (в режиме автоматического переключения пина или уход в прерывание и там делаем шаг) или делать шаги по millis\micros.
     
  16. Kakmyc

    Kakmyc Нерд

    Т.е. варианты:
    runToPosition()
    runToNewPosition() и
    runSpeedToPosition()
    Вообще не рассматривали ?
     
  17. Мы явно говорим о разных библиотеках, тут https://github.com/adafruit/AccelStepper/blob/master/AccelStepper.cpp, все вызывает runSpeed.
    1. runToPosition() вызывает в цикле while метод run() , а в нем идет вызов runSpeed().
    2. runToNewPosition() вызывает runToPosition(), смотрим пункт 1.
    3. runSpeedToPosition() вызывает runSpeed()

    Принцип работы AccelStepper достаточно прозрачен, что бы мы не вызывали мы придем в runSpeed(). В ней, таймер на основе millis() и вызов step(), который в моем случает вызовет step1 (для внешнего драйвера) и сделает один шаг. В этом плане они похоже с Gyverstepper.

    Это хорошая библиотека, но не для решения моей задачи.
     
  18. Разобрался с ускорением, все оказалась просто, были 2 проблемы, первая и основная ограничение тока на драйвере:), вторая более плавная регулировка ускорения.

    В этой версии, как и раньше работа с шаговым на основе таймера в режиме PWM (ICR1), без прерываний. Ускорение на основе micros, а так же удалил все не нужное, подсчет шагов и остановка по достижению заданной цели (в такой реализации, их лучше помещать в порывание, а у меня его нет).

    Файл .ino
    Код (C++):
    // Библиотека для работы с шаговым двигателем
    #include "StepperOnClosing.h"

    // Шаговый двигатель
    StepperOnClosing closingStepper = StepperOnClosing();

    void setup()
    {
      closingStepper.InitT();

      Serial.begin(115200);
    }

    void loop()
    {
      // Нужен для корректной работы ускорения
      closingStepper.Tick();
     
      if (Serial.available())
      {
        char ch = Serial.read();
        if (ch == 'q') closingStepper.Break();
        if (ch == 'w') closingStepper.Stop();
        if (ch == 'e') closingStepper.SetSpeed(4000);
        if (ch == 'r') closingStepper.SetSpeed(-4000);
        if (ch == 't') closingStepper.SetSpeed(0);
        if (ch == 'y') closingStepper.SetSpeed(8000);
        if (ch == 'u') closingStepper.SetSpeed(-8000);
      }
    }
    Остальное в прищепке.

    Что касается готовых библиотек, мне больше понравилась Gyverstepper, чем AccelStepper.
     

    Вложения:

    • StepperOnClosing.cpp
      Размер файла:
      10,4 КБ
      Просмотров:
      143
    • StepperOnClosing.h
      Размер файла:
      1,5 КБ
      Просмотров:
      146
    • StepperOnInterrupt.cpp
      Размер файла:
      127 байт
      Просмотров:
      136
    • StepperOnInterrupt.h
      Размер файла:
      1,2 КБ
      Просмотров:
      142
    • BitsMacros.h
      Размер файла:
      2,1 КБ
      Просмотров:
      139
    • Def.h
      Размер файла:
      673 байт
      Просмотров:
      133
    • GStypes.h
      Размер файла:
      402 байт
      Просмотров:
      141