Дергается шаговый двигатель при работе с LCD 1602

Тигран Оганджанян

✩✩✩✩✩✩✩
9 Апр 2020
14
4
Доброго времени суток! Столкнулся с проблемой при работе с шаговым двигателем на драйвере a4988 и дисплеем LCD 1602. При отправке теста на дисплей по каналу I2C, в момент обновления дисплея, шаговый двигатель замирает на доли секунды из за чего происходит рывок. Как от этого избавиться? Микрокотроллер Wemos mini.

C++:
#include <LCD_1602_RUS.h>
#include "GyverButton.h"
#include "GyverStepper.h"

LCD_1602_RUS lcd(0x27, 16, 2);
String empStr = "                ";
GStepper<STEPPER2WIRE> stepper1(400, D7, D8, D4);

GButton myButt1;
GButton myButt2;
GButton myButt3;
GButton myButt4;

float MotorSpeed=400, MotorSpeedSM; //максимум 10 об/сек. (10*400 шагов)
bool auto_falg=false, stop_flag=false;
float Odo;

void ICACHE_RAM_ATTR sens(); //прирывание
volatile unsigned long lastflash, flash, lastshow;
unsigned int RPS;

void setup()
{
  //Debug mode
  //Blynk
  //Encoder
   attachInterrupt(D6, sens, RISING);    // подключить прерывание на D6 пин при повышении сигнала
 
  //Buttion
  Serial.begin(9600);
  pinMode(D6, INPUT);

  myButt1.setType(LOW_PULL);
  myButt2.setType(LOW_PULL);
  myButt3.setType(LOW_PULL);
  myButt4.setType(LOW_PULL);
 
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Redy to work");
  lcd.setCursor(0, 1);
  lcd.print("Waiting...");

  //StepMotor
  stepper1.setRunMode(KEEP_SPEED);
  stepper1.setAcceleration(0);
  stepper1.disable();

  //Menu
}

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

  myButt1.tick(analog < 200 && analog > 160);
  myButt2.tick(analog < 330 && analog > 290);
  myButt3.tick(analog < 545 && analog > 505);
  myButt4.tick(analog < 730 && analog > 690);
 
  if (!auto_falg) { //Ессли ручной режим
    if (analog < 200 && analog > 160) {
      if (stop_flag) {stepper1.enable(); stepper1.setSpeed(MotorSpeed); lcd.setCursor(0, 0); lcd.print("Unclick to stop");}
      if (millis() - lastshow > 100) {
        MotorSpeedSM += (MotorSpeed-MotorSpeedSM)*0.2;
        stepper1.setSpeed(MotorSpeedSM);
        lastshow = millis();  // сброс таймера
      }
      stop_flag=false;
    }else{
      if (millis() - lastshow > 100 && MotorSpeedSM>10) {
        MotorSpeedSM += (0-MotorSpeedSM)*0.4;
        Serial.println(MotorSpeedSM);
        stepper1.setSpeed(MotorSpeedSM);     
        lastshow = millis();
        if (MotorSpeedSM<10 & !stop_flag) {
          stepper1.disable();
          lcd.setCursor(0, 0); lcd.print("Press to start");
          stop_flag=true;
        }
      }
    }
  }
  
  if (myButt2.isClick()) {
    MotorSpeed+=400;
    if (MotorSpeed>2400) MotorSpeed=2400;
    if (MotorSpeed==0) MotorSpeed=400;
    lcd.setCursor(0, 1); lcd.print(empStr); lcd.setCursor(0, 1); lcd.print("Speed: " + String(60*int(MotorSpeed)/1600)+ "RPM");
  }
  if (myButt3.isClick()) {
    MotorSpeed-=400;
    if (MotorSpeed<-2400) MotorSpeed=-2400;
    if (MotorSpeed==0) MotorSpeed=-400;
    lcd.setCursor(0, 1); lcd.print(empStr); lcd.setCursor(0, 1); lcd.print("Speed: " + String(60*int(MotorSpeed)/1600)+ "RPM");
  }
  if (myButt4.isClick()) {
    MotorSpeed=MotorSpeed*-1;
    lcd.setCursor(0, 1); lcd.print(empStr); lcd.setCursor(0, 1); lcd.print("Speed: " + String(60*int(MotorSpeed)/1600) + "RPM");
  }
  
  if (myButt4.isHolded()) {
    auto_falg = !auto_falg;
    stepper_enable();
  }

  if (auto_falg) {
    if (millis() - lastflash > 1000/20) { //Вывод скорости каждую 1/20 сек
      RPS = flash*20;
      Odo = Odo + 0.012*float(flash)/37;
      //Serial.println("Enc. speed:" + String(RPS) + "step/sec,  Flash psi:" + String(flash) + ",   Odo: " + String(Odo) );       // выводим RPS
      lastflash = millis();  // сброс таймера
      flash=0;
      stepper1.setSpeed(RPS);
      if (RPS==0) {stepper1.disable(); }
      if (millis() - lastshow > 2000) { //Вывод показаний одометра
        //Serial.println("Odometor:" + String(Odo) + "m");
        lcd.setCursor(0, 1); lcd.print("Odometor:" + String(Odo) + "m");
        lastshow = millis();  // сброс таймера
      }
    }
  }
  stepper1.tick();
}

void sens() {
  flash += 1;
}

void stepper_enable() {
    if (auto_falg) {
      stepper1.enable();
      Serial.print("Enable");
      lcd.setCursor(0, 0); lcd.print(empStr); lcd.setCursor(0, 0); lcd.print("Auto mode");
    }else{
      stepper1.disable();
      Serial.print("Disable");
      lcd.setCursor(0, 0); lcd.print(empStr); lcd.setCursor(0, 0); lcd.print("Manual mode");
    }
}
 

bort707

★★★★★★✩
21 Сен 2020
3,058
910
может потому что слишком часто на экран выводите - 20 раз в секунду?
 

Тигран Оганджанян

✩✩✩✩✩✩✩
9 Апр 2020
14
4
У гайвера есть библиотека, гайвер таймер. вот эту функцию: stepper1.tick(); надо вызывать по таймеру.
И с каким интервалом? Двигатель не будет дёргаться постоянно ?

@bort707, нет 1 раз в секунду. И каждую секунду происходит остановка приблизительно на 20 мск. Но в руках этот толчок ощущается...
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,262
1,300
Москва
Интервал подбирайте сами. Немо 17 у меня максимально работал на скорости 6 оборотов в скунду, я обычно ставлю 5 оборотов как максимум. Т.е. 1 тик выставляю так, что бы было 1000 раз в секунду. Это при целом шаге. Но! Если используются ускорения, то надо чаще. Страшного в этом ничего нет, т.к. тик следит за частотой сам и если время не пришло, то он ничего не делает. И ОЧЕНЬ возможно придется подобрать нужный таймер ;)
В тот момент , конда идет вывод на дисплей у Вас тики не происходят, увы. А если не проходят, то и дергается мотор.
 

Тигран Оганджанян

✩✩✩✩✩✩✩
9 Апр 2020
14
4
Вообщем провел небольшие эксперименты, для вывода строчки текста на дисплей требуется 16 000 микросекунд... Да даже для вывода одного символа требуется 2000 микросекунд. В момем случаи двигатель крутиться на максимальной скорости 6 об/сек. где 1 оборот 400 шагов. Следовательно на 1 шаг требуется 1 000 000 / 400*6 = 416 микросекунд. От сюда вывод, что как ни крути, но в случаи даже вывода данных на дисплей по одному символу двигатель будет пропускать до 5 шагов, от сюда и рывки...

Если ограничиться скоростью 5 об/сек и разрешением 200 шагом за оборот как рекомендует @Старик Похабыч, то получается чуть лучше (пропускаются два шага), но все равно чувствуется рывочки. А иногда мотор тупо застывает из за большой частоты...

Поэтому пока я вижу единственный выход выводить данные на экран когда мотор останавливается. Но как реализована управление на 3д принтерах? Там 3- шаговых мотора + большой LCD дисплей и нечего не тормозит!?
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,262
1,300
Москва
Я рекомендовал использовать перерывания по таймеру. А 5 я ставлю, т.к. мне хватало. Иногда даже много было.
Вот видео где мотор управляется кнопками с одновременным выводом информации на LCD. Тик мотора вызывается по таймеру.
 

bort707

★★★★★★✩
21 Сен 2020
3,058
910
Поэтому пока я вижу единственный выход выводить данные на экран когда мотор останавливается. Но как реализована управление на 3д принтерах? Там 3- шаговых мотора + большой LCD дисплей и нечего не тормозит!?
используйте прерывание аппаратного таймера и ничего дергаться не будет. Именно так это организовано в промышленных устройствах
 

Тигран Оганджанян

✩✩✩✩✩✩✩
9 Апр 2020
14
4
Всем большое спасибо! Получилось! Да только вчера узнал про термин прерывание аппаратного таймера! То что это не просто таймеры в цикле Loop().
Итак вот полезна информация тем, кто столкнется с той же проблемой:

А вот информация именно для микроконтроллеров на базе ESP8266 (Wemos mini, Node MCU и т.д.), к сожалению у них с таймерами все грустно! но 1 мне удалось создать по инструкции из этого видео: