GyverStepper. Обсуждение библиотеки

AlexGyver

★★★★★★✩
Команда форума
30 Июл 2018
359
574

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

★★★★★★★
14 Авг 2019
4,281
1,306
Москва
Да, и осциллографом можно зацепить, но хотелось бы услышать от автора как там все происходит и в вольтах. Может я чего не знаю про ту плату ? Я даже не знаю какая у него плата. Полезно для расширения кругозора. И я сейчас не шучу.
 

AlexGyver

★★★★★★✩
Команда форума
30 Июл 2018
359
574
@Старик Похабыч, думаю там обычный GRBL шилд, в котором все драйвера и провода друг на друга шумят
 
Изменено:

AlexGyver

★★★★★★✩
Команда форума
30 Июл 2018
359
574
@YPAL, помехи не равно низкое напряжение, и напряжение поднимать не нужно. Нужно заняться экранированием силовых проводов и моторов и цеплянием экрана на землю. В этом проблема самоделочных станков: как провода подключить на схеме нарисовано, как как их подключить чтобы схема работала - не написано. Помехи, индуктивные выбросы, хреновые шумящие блоки питания - это всё лучшие друзья самодельщика станков =)
 

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

★★★★★★★
14 Авг 2019
4,281
1,306
Москва

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

★★★★★★★
14 Авг 2019
4,281
1,306
Москва

YPAL

✩✩✩✩✩✩✩
14 Сен 2020
4
0
Да, и осциллографом можно зацепить, но хотелось бы услышать от автора как там все происходит и в вольтах. Может я чего не знаю про ту плату ? Я даже не знаю какая у него плата. Полезно для расширения кругозора. И я сейчас не шучу.
Станок на arduino uno пробую собрать. Буду закупаться экранированными проводами. Завтра если времени хватит сниму сигнал с той платы где 5В.
 

Deiman12

✩✩✩✩✩✩✩
26 Сен 2020
4
0
Здравствуйте!
Столкнулся с такой проблемой, решение которой от меня все ускользает.
Ситуация такова:
Строю станочек и уже победил панель кнопок и экран (почему-то думал, что это самое сложное... ошибался), остался PID и Шаговик. С PID еще все впереди, (оставил на мякотку на потом), а вот шаговик, не смотря на кажущуюся простоту, мне не дается прям вообще. А если конкретнее, то пробелам в одной детали, нужно менять скорость вращения в шагах. У меня есть переменная SPS, она меняется в большую и меньшую сторону кнопками вверх и вниз, меняется она 100%, ибо это видно на экране. .setSpeed(SPS) ну никак не влияет на скорость двигателя. Код прикладываю весь что сейчас есть, проблемные места находятся между комментариями отражающими мой крик.
Я конечно подозреваю, что это из-за того, что я первый раз кодить 3 дня назад начал, но не уверен.
Если нужно будет пояснить места говнокода, то это я смогу.

C++:
#include "GyverStepper.h" // инициализация библиотеки шагового двигателя
#include <LiquidCrystal.h>  // инициализация LCD библиотеки

#define steps 1600 // 1600 - шагов на оборот с учетом деления шага
#define STEP 2
#define DIR 5
#define EN 8
    GStepper< STEPPER2WIRE> stepper1(steps, STEP, DIR, EN); //Настройка портов для библиотеки шаговых двигателей

    LiquidCrystal lcd(9, 10, 3, 4, 6, 7);  // Пины смещены, что-бы не пересекатся с платой расширения для шаговика


// Имена кнопок клавиатуры на дисплее
#define btnUP   1
#define btnDOWN 2
#define btnLEFT 3
#define btnRIGHT 4
#define btnSELECT 5
#define btnNONE 10


// Условия разбивки одного порта на 5 кнопок
int detectButton() {
  int keyAnalog =  analogRead(A0);
  if (keyAnalog < 100) {
    // Значение меньше 100 – нажата кнопка right
    return btnRIGHT;
  } else if (keyAnalog < 200) {
    // Значение больше 100 (иначе мы бы вошли в предыдущий блок результата сравнения, но меньше 200 – нажата кнопка UP
    return btnUP;
  } else if (keyAnalog < 400) {
    // Значение больше 200, но меньше 400 – нажата кнопка DOWN
    return btnDOWN;
  } else if (keyAnalog < 600) {
    // Значение больше 400, но меньше 600 – нажата кнопка LEFT
    return btnLEFT;
  } else if (keyAnalog < 800) {
    // Значение больше 600, но меньше 800 – нажата кнопка SELECT
    return btnSELECT;
  } else {
    // Все остальные значения (до 1023) будут означать, что нажатий не было
    return btnNONE;
  }
}

#define btnRST 11  // Присвоение имени и пина кнопке Reset

// Начальные значения коэффициентов

 int SPS = 1600; // Начальное количество шагов в секунду
 int SPSMin = 950; // Минимальные обороты двигателя
 int SPSMax = 10000; //  максимальные обороты двигателя
 int AccelerationStepper = 30000; // ускорение двигателя
 int revStepperSpeed = 10000; // Скорость реверса
 int tempIn = 0;  // Температура с датчика
 int tempS = 320; // Начальная целевая температура
 int tempMin = 20; // минимально устанавливаемая температура
 int tempMax = 360; // максимально устанавливаемая температура

 long posStepper = 2147483600; // крайняя позиция двигателя, после чего будет остановлен



// Флажки кнопок
 boolean btnUP_flag = 0;   // для задержки срабатывания кнопки UP
 boolean btnDOWN_flag = 0; // для задержки срабатывания кнопки DOWN
 boolean btnRIGHT_flag = 0; // для задержки срабатывания кнопки RIGHT
 boolean btnLEFT_flag = 0; // для задержки срабатывания кнопки LEFT
 boolean btnSELECT_flag = 0; // для задержки срабатывания кнопки SELECT



boolean btnRSTS;   // храним состояния кнопок (S - State)
boolean btnRSTF;   // флажки кнопок (F - Flag)
boolean btnRSTR;   // флажки кнопок на отпускание (R - Release)
boolean btnRSTP;   // флажки кнопок на нажатие (P - Press)
boolean btnRSTH;   // флажки кнопок на удержание (многократный вызов) (H - Hold)
boolean btnRSTHO;  // флажки кнопок на удержание (один вызов при нажатии) (HO - Hold Once)
boolean btnRSTD;   // флажки кнопок на двойное нажатие (D - Double)
boolean btnRSTDP;  // флажки кнопок на двойное нажатие и отпускание (D - Double Pressed)

unsigned long btnRST_timer; // таймер последнего нажатия кнопки
unsigned long btnRST_double; // таймер двойного нажатия кнопки

// Задержки кнопок
 unsigned long button_timer; // Счетчик для замера времени удержания
 

// задержки кнопок
 #define double_timer 150   // время (мс), отведённое на второе нажатие
 #define hold 500           // время (мс), после которого кнопка считается зажатой
 #define debounce 100       // (мс), антидребезг и уменьшение скорости набора
 #define pause_timer 2000    // Долгое нажатие для переключения режима

// Скорости изменения коэффициентов
#define spsSpeed 100      // скорость изменения SPS (шагов  в секунду)
#define tempSSpeed 1    // скорость изменения целевой температуры

 
 boolean stopStepper = 0; // вкл и откл двигателя. в норме выключен при включении питания
 boolean revStepper = 0; // Обратный ход шаговика

 boolean stopTerm = 0; //вкл и откл нагревателя. в норме выключен при включении питания

 void setup() {

  pinMode (btnRST, INPUT); // режим кнопки Reset
 

  pinMode(btnRST, INPUT_PULLUP);

// Для экрана
  Serial.begin(9600);
  lcd.begin(16, 2);
 
// ААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААА
stepper1.setRunMode(KEEP_SPEED); // режим поддержания скорости
  stepper1.setSpeed(666);        //
// ААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААА

}

 void loop() {

  Serial.print(SPS);
// ААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААА
stepper1.tick();
  // сделаем таймер на 50 мс и будем опрашивать потенциометр
  // менять скорость чаще нет смысла
  static uint32_t tmr2;
  if (millis() - tmr2 > 50) {
    tmr2 = millis();
    // ставим новую скорость (-512.. 512 шагов в секунду)
    // будет крутиться в разные стороны
    stepper1.setSpeed(SPS);
    stepper1.tick();
    }
// ААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААА

  //-------опрос кнопок--------
  btnRSTS = !digitalRead(btnRST);
  buttons(); //отработка кнопок
  //-------опрос кнопок--------

  // отработка режимов (опускание флага обязательно!)
  if (btnRSTP) {
    Serial.println("pressed");
    btnRSTP = 0;
  }
  if (btnRSTD) {
    Serial.println("double");
    btnRSTD = 0;
  }
  if (btnRSTH && btnRSTHO) {
    Serial.println("hold once");
    Serial.println(revStepper);
    Serial.println(stopStepper);
    btnRSTHO = 0;
      }

      

 


 
// Отображение преременной SPS на экране
  lcd.setCursor(0, 0);
        lcd.print("SPS ");
        lcd.print(SPS);
  // Часть кода убирающая
    if (SPS < 10) {
        lcd.print(" ");
  }
    else if (SPS >= 10 && SPS < 100) {
        lcd.print(" ");
        }
    else if (tempS >= 100 && tempS < 1000) {
        lcd.print("   ");
  }
 
 
  // Отображение целевой температуры на нагревателе
  lcd.setCursor(0, 1);
        lcd.print("Ts ");
        lcd.print(tempS);
    if (tempS < 10) {
        lcd.print(" ");
  }
    else if (tempS >= 10 && tempS < 100) {
        lcd.print(" ");
    }


   // Отображение Текущей температуры с термистора
  lcd.setCursor(7, 1);
        lcd.print("T ");
        lcd.print(tempIn);
    if (tempS < 10) {
        lcd.print(" ");
  }
    else if (tempS >= 10 && tempS < 100) {
        lcd.print(" ");
    }
    // отбражение текущего режима двигателя
  lcd.setCursor(13, 0);
       if (stopStepper == 0 && revStepper == 0) {
        lcd.print("off");}
        if (revStepper == 1 && stopStepper == 0){
         lcd.print("rev");}
       else if (stopStepper == 1 && revStepper == 0){
         lcd.print("on "); }
      

      // отображение текущего состояния нагревателя
  lcd.setCursor(13, 1);
       if (stopTerm == 0) {
        lcd.print("off");}
       else if (stopTerm == 1){
         lcd.print("on "); }
        
 
// Обработчик нажатий кнопок подключенных к пину А0. Необходим, т.к. подключено 5 кнопок к ожному пину
  int button = detectButton();

  switch (button) {
    case btnUP:
      if (btnUP_flag == 0 && millis() - button_timer > debounce && tempS < tempMax) // Значекние button_timer задает скорость изменение переменной путем задержки между нажатиями
         {
           tempS += tempSSpeed; // Увеличение Целевой температуры на величину tempSSpeed
           btnUP_flag = 1;
           button_timer = millis();
     }
     if (btnUP_flag == 1 && millis() - button_timer > debounce) //Благодаря этой части, код выполняется постоянно пока нажата кнопка
        {
           btnUP_flag = 0;
        }
  break;



    case btnDOWN:
      if (btnDOWN_flag == 0 && millis() - button_timer > debounce && tempS > tempMin) // Значекние button_timer задает скорость изменение переменной путем задержки между нажатиями
        {
           tempS -= tempSSpeed; // Уменьшение Целевой температуры на величину tempSSpeed
           btnDOWN_flag = 1;
           button_timer = millis();
        }
     if (btnDOWN_flag == 1 && millis() - button_timer > debounce)
        {
           btnDOWN_flag = 0;
        }
     break;
    
    case btnLEFT:
      if (btnLEFT_flag == 0 && millis() - button_timer > debounce && SPS > SPSMin) // Значекние button_timer задает скорость изменение переменной путем задержки между нажатиями
        {
           SPS -= spsSpeed; // уменьшение SPS (Скорости двигателя) на величину spsSpeed
           btnLEFT_flag = 1;
           button_timer = millis();       
        }
     else if (btnLEFT_flag == 1 && millis() - button_timer > debounce) {
           btnLEFT_flag = 0;
      }
     break;
 
    case btnRIGHT:
      if (btnRIGHT_flag == 0 && millis() - button_timer > debounce && SPS < SPSMax) // Значекние button_timer задает скорость изменение переменной путем задержки между нажатиями
        {
           SPS += spsSpeed; // увеличение SPS (Скорости двигателя) на величину spsSpeed
           btnRIGHT_flag = 1;
           button_timer = millis();
        }
      if (btnRIGHT_flag == 1 && millis() - button_timer > debounce)
         {
           btnRIGHT_flag = 0;

         }
  break;

    case btnSELECT:
           if (btnSELECT_flag == 0 && millis() - button_timer > pause_timer) /* Значекние button_timer задает скорость изменение переменной путем задержки между нажатиями
           pause_timer - необходим доя медленной смены значений флажков, при малом значении сменяется слишком быстро*/
        {
           stopTerm = !stopTerm;   // переключение состояния нагревателя
           btnSELECT_flag = 1;
           button_timer = millis();
        }
     if (btnSELECT_flag == 1 && millis() - button_timer > pause_timer)
        {
           btnSELECT_flag = 0;
        }
  break;

    default:
      //printDisplay("Press any key");
      break;     
  }
}

// обрабортчик кнопки RST. делает одинарное/двойное/удержание
void buttons() {
  //-------------------------btnRST--------------------------
  // нажали (с антидребезгом)
  if (btnRSTS && !btnRSTF && millis() - btnRST_timer > debounce) {
    btnRSTF = 1;
    btnRSTHO = 1;
    btnRST_timer = millis();
  }
  // если отпустили до hold, считать отпущенной
  if (!btnRSTS && btnRSTF && !btnRSTR && !btnRSTDP && millis() - btnRST_timer < hold) {
    btnRSTR = 1;
    btnRSTF = 0;
    btnRST_double = millis();
    
  }
  // если отпустили и прошло больше double_timer, считать 1 нажатием
  if (btnRSTR && !btnRSTDP && millis() - btnRST_double > double_timer) {
    btnRSTR = 0;
    btnRSTP = 1;
    stopStepper = !stopStepper; // переключение состояния рабоы двигателя on/off
  }
  // если отпустили и прошло меньше double_timer и нажата снова, считать что нажата 2 раз
  if (btnRSTF && !btnRSTDP && btnRSTR && millis() - btnRST_double < double_timer) {
    btnRSTF = 0;
    btnRSTR = 0;
    btnRSTDP = 1;
  
  }
  // если была нажата 2 раз и отпущена, считать что была нажата 2 раза
  if (btnRSTDP && millis() - btnRST_timer < hold) {
    btnRSTDP = 0;
    btnRSTD = 1;
    btnRST_timer = millis();
    revStepper = !revStepper; // переключение залипания реверса при двойном клике
  }
  // Если удерживается более hold, то считать удержанием
  if (btnRSTF && !btnRSTD && !btnRSTH && millis() - btnRST_timer > hold) {
    btnRSTH = 1;
    revStepper = !revStepper; // Включение реверса при зажатой кнопке
  }
  // Если отпущена после hold, то считать, что была удержана
  if (!btnRSTS && btnRSTF && millis() - btnRST_timer > hold) {
    btnRSTF = 0;
    btnRSTH = 0;
    revStepper = !revStepper; // отключение удержания реверса при отпускании кнопки
    btnRST_timer = millis();
  }
}
Ах да, забыл уточнить. Пробовал разные режимы работы, FOLLOW_POS и KEEP_SPEED, результат - отсутствует.

UPD: Оказалось, что такое непонятное вращение и-за вывода информации на экран. (Скорость всё-равно не понятно меняется, но хотя бы меняется)
Теперь не понятно как выводить информацию, если добавляю код вывода на экран в void loop (), перестает нормально работать двигатель.
 

AlexGyver

★★★★★★✩
Команда форума
30 Июл 2018
359
574
@Deiman12, повесить обработчик мотора (тик) на прерывания таймера, как в примерах
 

Deiman12

✩✩✩✩✩✩✩
26 Сен 2020
4
0
@Deiman12, повесить обработчик мотора (тик) на прерывания таймера, как в примерах
Я же говорил, что я не грамотный. Немного не понял куда его воткнуть.
Использую немного измененную версию тестового кода, и двигатель раскручивается и меняется скорость, если не выводить ничего на дисплей, но максимальная скорость ограничена. Если полностью очистить loop и оставить только код двигателя, то вращается лучше. Любое добавление в loop подтормаживает двигатель.
Насколько я понял, из-за последовательность обработки команд тормозит.
Думал попробовать на FreeRTOS.




UPD: Уже повтыкал везде stepper1.tick(); скорость стала немного выше но все-равно очень медленно. Втыкал после каждой команды

C++:
// ААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААА


  stepper1.enable();
  stepper1.autoPower(true);
  // установка ускорения в шагах/сек/сек
  stepper1.setAcceleration(1200);
  stepper1.setRunMode(KEEP_SPEED);
// ААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААА

}
bool btnState = false;
 void loop() {


// ААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААА
 
  // кнопка нажата
  if (btnRST && !btnState) {
    btnState = true;
    stepper1.setSpeed(10000);
  }
  // кнопка отпущена
  if (btnRST && btnState) {
    btnState = false;
    stepper1.stop();
    }
  
  //АААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААААА
 

AlexGyver

★★★★★★✩
Команда форума
30 Июл 2018
359
574
  • Лойс +1
Реакции: Deiman12

Deiman12

✩✩✩✩✩✩✩
26 Сен 2020
4
0
@Deiman12,

ну я же пишу, как в примере. Подробнее о прерываниях по таймеру можно почитать в уроках, там всё максимально подробно расписано
Спасибо большое, все заработало как надо, работают все функции которые нужно.
При частоте прерывания в 45000 Гц все норм работает, при частоте выше 50000 Гц стабильнее набор высоких оборотов и они немного выше, но перестает отвечать код отвечающий за экран и кнопки. Но мне такой результат пока устраивает.
Надеюсь ПИД все не сломает.

Еще раз благодарю!
 

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

★★★★★★★
14 Авг 2019
4,281
1,306
Москва
1 000 000 / 45 000 = 22.2 мкс
Это время одного шага . Если взять для примера мотор 28byj-48, который имеет 2048 шагов, и заявленную скорость в 15 оборотов в минуту, то можно рассчитать, что: 1) 1 оборот делается за 4 секунды, тогда на шаг нужно 2 мкс. Значит максимальная скорость достигнута не будет, для макс. скорости нужна частота 500кГц. Однако мой мотор не смог достигнуть такой скорости. максимум , что вышло адекватно получить это 9 оборотов в минуту с копейками. Что тоже вполне укладывается в 22.мкс. Но , как автор уже заметил, повышение частоты уменьшает время на обработку всего остального кода. Тут уже упираемся в пределы ардуины.
 

AlexGyver

★★★★★★✩
Команда форума
30 Июл 2018
359
574
@Deiman12, нужно брать мотор с меньшим количеством шагов на оборот (х редуктор), чтобы работать на более низких скоростях прерываний, или лезть ковырять уже библиотеки дисплеев и кнопок, чтобы оно все работало в условиях частых прерываний на мотор. Пид - чисто математика, он будет работать в любом случае)
 

Deiman12

✩✩✩✩✩✩✩
26 Сен 2020
4
0
1 000 000 / 45 000 = 22.2 мкс
Это время одного шага . Если взять для примера мотор 28byj-48, который имеет 2048 шагов, и заявленную скорость в 15 оборотов в минуту, то можно рассчитать, что: 1) 1 оборот делается за 4 секунды, тогда на шаг нужно 2 мкс. Значит максимальная скорость достигнута не будет, для макс. скорости нужна частота 500кГц. Однако мой мотор не смог достигнуть такой скорости. максимум , что вышло адекватно получить это 9 оборотов в минуту с копейками. Что тоже вполне укладывается в 22.мкс. Но , как автор уже заметил, повышение частоты уменьшает время на обработку всего остального кода. Тут уже упираемся в пределы ардуины.
Тут еще напряжение двигателя нужно смотреть. Для высоких оборотов нужно достаточно высокое напряжение, на меньшем не успевают перемагничиваться обмотки. Есть даже упрощенная формула расчета напряжения U = 32 * корень индуктивности обмотки. Из нее видно что, для полноценной работы на высоких скоростях с нормальным моментом нужно достаточно высокое напряжение.


@Deiman12, нужно брать мотор с меньшим количеством шагов на оборот (х редуктор), чтобы работать на более низких скоростях прерываний, или лезть ковырять уже библиотеки дисплеев и кнопок, чтобы оно все работало в условиях частых прерываний на мотор. Пид - чисто математика, он будет работать в любом случае)
Для больших скоростей могу просто уменьшить деление шага.
 

Евгений.Ш

✩✩✩✩✩✩✩
1 Фев 2020
3
0
Здравствуйте. Подскажите по коду. Мне нужно регулировать скорость вращения шагового двигателя, переменным резистором. Резистор 10Ком. Высокие обороты нормально регулируются, а вот на низких скорость остается большей чем мне нужно. Моторчик 17HS4401

C++:
// драйвер STEP-DIR

#include <GyverStepper.h>
GStepper<STEPPER2WIRE> stepper(200, 2, 3, 4);
// 2 - STEP
// 3 - DIR
// 4 - EN

void setup() {
  stepper.setRunMode(KEEP_SPEED); // режим поддержания скорости
  stepper.setSpeedDeg(50);        // в градусах/сек
}
void loop() {
  stepper.tick();
  // сделаем таймер на 50 мс и будем опрашивать потенциометр
  // менять скорость чаще нет смысла
  static uint32_t tmr2;
  if (millis() - tmr2 > 50) {
    tmr2 = millis();
    // ставим новую скорость (-512.. шагов в секунду)
    // будет крутиться в разные стороны
    stepper.setSpeed(-512 - analogRead(0));
  }
}
 

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

★★★★★★★
14 Авг 2019
4,281
1,306
Москва
вот это плохая идея на мой взгляд:
-512 - analogRead(0)
Известно, что потенциометр дает значения от 0 до 1023
Допустим есть гранцы оборотов от 4 до 35 чисто для примера. Тогда можно записать
stepper.setSpeed(map(analogRead(A0),0,1023,4,35))
И еще , в setSpeed() передается значение , какого типа ?
 
Изменено:

Евгений.Ш

✩✩✩✩✩✩✩
1 Фев 2020
3
0
Не знаю. Я новичок. Код взят из примеров в библиотеке.
если ставлю положительное значение 512
stepper.setSpeed(512 - analogRead(0))
то моторчик регулируется до определенного значения и начинает крутится в другую строну.
Попробовал сделать как вы написали регулировка на низкой скорости стала то что нужно. Спасибо.
Объясните если не трудно, что означают эти цифры 0,23,4,35 ?
 
Изменено:

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

★★★★★★★
14 Авг 2019
4,281
1,306
Москва
Опечатка там, подправил..
0,1023 - это минимальное и максимальное значение , которое может выдать функция analogRead
4, 35 это минимальная и максимальная скорость которую я хочу получить.
setSpeed получает значение типа int, т.е. 2 байта со знаком. т.е. от -16384 до 16383.
При записи вида 512 - analogRead(0) можно получить значения от -511 до 512. Вот и движение в разные стороны. При записи -512 -analogRead(0) получаются значения от - 512 до - 1535. Вот и движение в одну сторону, без возможности двигаться медленно. Это математика...
 
  • Лойс +1
Реакции: Евгений.Ш

Евгений.Ш

✩✩✩✩✩✩✩
1 Фев 2020
3
0
[B]Старик Похабыч[/B] спасибо. Подскажите как минимальная скорость 4 соотносится с количеством оборотов. Как можно посчитать сколько оборотов в минуту делает моторчик на минимальной скорости 4 и на максимальной скорости 35?