Управление скоростью шагового двигателя

Вопросы об управлении шаговыми двигателями
Vladimir55
Сообщения: 4
Зарегистрирован: 12 фев 2017, 21:32

Управление скоростью шагового двигателя

Сообщение Vladimir55 » 17 фев 2020, 17:31

Здравствуйте.
Пытаюсь управлять скоростью шагового двигателя, переопределяя делитель частоты в основном цикле, но получается неадекватная частота вращения. Подскажите как правильно управлять частотой вращения? Переопределять делитель только в обработчике прерываний?
В коде пин 2 использую для старт стопа двигателя.

//шаговый двигатель с драйвером степ-дир
//в простейшем случае вращения с постоянной скоростью без ускорения
//09.02.20
//***********************************************//


Код: Выделить всё

#include <TimerOne.h>
#include <StepDirDriver.h>

StepDirDriver myMotor(10, 11, 12);  // создаем объект типа StepDirDriver, задаем выводы для сигналов
             
  #include <MsTimer2.h>  //библиотека прерывания по таймеру
     // обработчик прерывания
  #include <Button.h>   //библиотека обработки сигнала датчика типа кнопка
  #define  GORELKA_PIN20 2 //к этому пину подключаем контакты реле управляемого напряжением включения горелки (220 В).К пину подтянут +5в через 10кОм на нем высокое Пин
                           //становится активен , если замыкается на GND
  int N = 10; //количество считываний кнопок в объектах типа кнопка для формирования массива осреднения в методе AWERAGE библиотеки <Button.h>
              //это же и количество пустых циклов loopв начале программы для корректной работы метода AWERAGE
  Button gorelka4(GORELKA_PIN20, N); //создание объекта gorelka4 контактов реле управляемого включением горелки (типа кнопка)
  volatile bool prevStatePress4 = false; //признак предыдущего состояния контактов реле, управляемого напряжением включения горелки (синими проводами), перед фактом
                                //размыкания контактов
  bool pin2; //переменная характеризующая пин 2
 int divider;

void setup()
{
 // Serial.begin(9600);
 
  pinMode(GORELKA_PIN20, INPUT); //пин к которому присоединены (НР) контакты реле, управляемого включением горелки (появление напряжения на синих проводах)
  //pinMode(2, INPUT);   
 Timer1.initialize(250);  // инициализация таймера 1, период 250 мкс = 0.25 мс
  Timer1.attachInterrupt(timerInterrupt, 250);  // задаем обработчик прерываний
  myMotor.setMode(0, false);  // шаговый режим, без фиксации при остановке
  myMotor.setDivider(10);     // делитель частоты 10 (1 оборот в сек) для двигателя с 1 об = 400 полушагов и и частотой прерываний 250 мкс
                              //Rpm = 60 000  /   ( divider  * Tcontrol * Nдвигателя * Nмикрошагов)
                              //Rpm – скорость вращения в оборотах в минуту;
                              //Tcontrol – период вызова метода control() в мс; В нашем случае 250 мкс = 0.25мс
                              //Nдвигателя – число шагов двигателя на полный оборот; В нашем случае 200 шагов/оборот
                              //Nмикрошагов – число микро шагов двигателя на одну фазу. Для шагового режима Nмикрошагов = 1, для полу шагового Nмикрошагов = 2 и т.д
                              //в нашем случае Nмикрошагов = 2
  myMotor.step(0);  // начальный запуск
 
  MsTimer2::set(2,timerInterupt2); // задаем период прерывания по таймеру 2 мс
  MsTimer2::start();              // разрешаем прерывание по таймеру
  }
  //-------------------------------------- обработчик прерывания 0,25 мс по первому таймеру
void  timerInterrupt()
 {
  //myMotor.setDivider(divider);
  myMotor.control(); // управвление двигателем
 }
 //----------------------------------------------------------------------------------------------       
                    //функция прерывания по таймеру 2
                               void  timerInterupt2()
 {
 
  gorelka4.filterAvarage();  // вызов метода фильтрации по среднему для контактов реле, управляемого напряжением включения горелки
 
 
              if (gorelka4.flagPress == false)
                                {
                                  //Serial.println("gorelka4.flagPress  ");Serial.println(gorelka4.flagPress);
                                  myMotor.step(0);//останов двигателя
                                }
                               if (gorelka4.flagPress == true) //включилась горелка и контакты реле, управляемого синими проводами горелки замкнулись (на пине 4 стало низкое)
                                {
                                  //divider = 17;
                                  //myMotor.setDivider(divider);
                                  myMotor.step(80000);//запуск двигателя на большое число шагов для непрерывного вращения
                                } 
 }       
//------------------------------------------------------------------------------------------------------
void loop()
{
  divider = 17;
  myMotor.setDivider(divider);
 

}


Эдуард
Администратор
Сообщения: 484
Зарегистрирован: 30 окт 2016, 20:53

Re: Управление скоростью шагового двигателя

Сообщение Эдуард » 17 фев 2020, 17:43

Здравствуйте!
Посмотрите по поводу установки скорости вращения урок 29. Там описывается другая библиотека, но метод тот же.
В вашем случае при myMotor.setDivider(20) и периоде таймера 0,25 мс будет.
Период переключения фаз 20 * 0,25 = 5 мс. Один оборот при двигателе с 200 шагами происходит за 5 * 200 = 1000 мс или 1 сек. Т.е. 60 оборотов в минуту. Если включен режим микрошагов, то надо соответственно скорректировать число шагов двигателя.

Vladimir55
Сообщения: 4
Зарегистрирован: 12 фев 2017, 21:32

Re: Управление скоростью шагового двигателя

Сообщение Vladimir55 » 17 фев 2020, 19:07

Спасибо за Вашу быструю реакцию.
Вопрос состоит в том, как менять значения делителя для управления скоростью. Мне нужно оперативно с кнопок LCD поменять частоту в реальном времени. Все параметры влияющие на частоту заданы в обработчике прерываний. Как реально управлять частотой?
Спасибо.

Эдуард
Администратор
Сообщения: 484
Зарегистрирован: 30 окт 2016, 20:53

Re: Управление скоростью шагового двигателя

Сообщение Эдуард » 17 фев 2020, 19:44

Вызывать метод myMotor.setDivider().

Vladimir55
Сообщения: 4
Зарегистрирован: 12 фев 2017, 21:32

Re: Управление скоростью шагового двигателя

Сообщение Vladimir55 » 17 фев 2020, 20:37

Написал сообщение, отправил, но оно пропало. Повторюсь.
Вызов метода - это понятно. В каком месте программы? Получается только в обработчике прерываний. Проблему решил. Все получилось как замышлял.
Прилагаю рабочий код управления приводом подачи топлива в горелку (может кому пригодится).
Эдуард, Вам низкий поклон. Вы замечательный человек! Дай Бог Вам здоровья!

Код: Выделить всё

//шаговый двигатель с драйвером степ-дир
//в простейшем случае вращения с постоянной заданной скоростью без ускорения
//17.02.20
//Управление скоростью шагового двигателя происходит переопределением делителя частоты divider с кнопок LCD в режиме стоп двигателя
//***********************************************//
 #define SOUND_PIN A5 //бузер для нажатия аналоговых кнопок
 #define FREQ 500   //частота звука при нажатии кнопки
 #define T_BUZ 300  //длительность звука при нажатии кнопки в миллисекундах
//***********************************************//
               //====Подключаем LCD-дисплей=====//
  #include <LiquidCrystal.h>
  LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
 #define BACKLIGHT_PIN 10     //Подсветка управляется через пин D10
              //=================================//
  #include <TimerOne.h>
  #include <StepDirDriver.h>

StepDirDriver myMotor(11, 12, 13);  // создаем объект типа StepDirDriver, задаем выводы для сигналов 11 - step+, 12 - Dir+, 13 - EN+
                // Подключаем библиотеку для работы с ARDUINO EEPROM
                //уставки температур будет храниться по адресам 0,1,2 время непрерывной работы маслонасоса по адресу 3
  #include <EEPROM.h>
  #include <MsTimer2.h>  //библиотека прерывания по таймеру
  #include <avr/wdt.h>   //библиотека сторожевого таймера
   // обработчик прерывания
  #include <Button.h>   //библиотека обработки сигнала датчика типа кнопка
  #define  GORELKA_PIN20 2 //к этому пину подключаем контакты реле управляемого напряжением включения горелки (220 В).К пину подтянут +5в через 10кОм на нем высокое Пин
                           //становится активен , если замыкается на GND
  int N = 10; //количество считываний кнопок в объектах типа кнопка для формирования массива осреднения в методе AWERAGE библиотеки <Button.h>
              //это же и количество пустых циклов loopв начале программы для корректной работы метода AWERAGE
  Button gorelka4(GORELKA_PIN20, N); //создание объекта gorelka4 контактов реле управляемого включением горелки (типа кнопка)
  volatile bool prevStatePress4 = false; //признак предыдущего состояния контактов реле, управляемого напряжением включения горелки (синими проводами), перед фактом
                                //размыкания контактов
  bool pin2; //переменная характеризующая пин 2
         
  volatile unsigned long StartWorkGorelka; //время старта горелки (включения синих проводов) в миллисекундах
                 //-----------Объявим переменные для задания задержки----------------
  unsigned long lastouttime = millis(); //переменная для организации циклов вывода на экран с интервалом T_ind 1 сек
  int T_ind = 1000; //интервал вывода на индикацию для исключения мерцания
  volatile unsigned long lastouttimeBUZ = millis(); //переменная для организации циклов длительности пещалки
  int T_buz = 500; //длительность пищалки при нажатии  аналоговой кнопки
  unsigned long lastT_keyCodeA_5 = millis(); //переменная для организации коррекции уставок при нажатии клавиши СЕЛЕКТ в пределах интервала maxT_keyCodeA_5
  int maxT_keyCodeA_5 = 15000; //интервал в миллисекундах в пределах которого можно корректировать уставку
                //-----------------------------------------------------------------------------
       //.............Аналоговая клавиатура подключена к пину A0............
  #define KEYPAD_PIN A0
  volatile int KeyValue2=0;  //значение нажатой кнопки используется для определения нажатой кнопки в функции int ReadKey(int keyPin)
  volatile int KeyNumPrev=0; //код аналоговой кнопки на предыдущем шаге программы
  volatile int buttonCount=0; //счетчик повторений кода нажатой аналоговой кнопки для выявления устойчивого ее состояния
  volatile int SUM_BUTTON_COUNT=5; //количесво повторений кода аналоговой кнопки при котором считается, что кнопка устойчиво нажата
  volatile int keyCodeA=0; //код (номер) аналоговой кнопки определяется в подпрограмме  ReadKey() фунуции опроса аналоговой клавиатуры
  volatile int keyCode=0; //код нажатой кнопки аналоговой клавиатуры
  volatile bool flagPressA=false; //флаг нажатия кнопки аналоговой клавиатуры
  volatile bool flagClickA = false; // клик  (момент) нажатия кнопки аналоговой клавиатуры
  volatile int numKey5=0; // текущий номер нажатия клавиши селект (клавиши 5). Если numKey5=1 - редактируем уставку скорости маслонасоса
      //......................................................................
     
float r_min=0.0; //заданная скорость в пересчете на об/мин
int T_ind1 = 15000; //интервал в ms до выполнения условного оператора после которого
                   //меняется значение заданной скорости
int T_ind2 = 20000;
int divider; //делитель частоты управления обмотками шагового двигателя
                 
void setup()
{
 // Serial.begin(9600);
 
  pinMode(GORELKA_PIN20, INPUT); //пин к которому присоединены (НР) контакты реле, управляемого включением горелки (появление напряжения на синих проводах)
  Timer1.initialize(250);  // инициализация таймера 1, период 250 мкс = 0.25 мс Для управления коммутацией обмоток шагового двигателя
  Timer1.attachInterrupt(timerInterrupt, 250);  // задаем обработчик прерываний
  myMotor.setMode(0, false);  // шаговый режим, без фиксации при остановке
  myMotor.setDivider(10);     // делитель частоты 10 (1 оборот в сек) для двигателя с 1 об = 400 полушагов и и частотой прерываний 250 мкс
                              //Rpm = 60 000  /   ( divider  * Tcontrol * Nдвигателя * Nмикрошагов)
                              //Rpm – скорость вращения в оборотах в минуту;
                              //Tcontrol – период вызова метода control() в мс; В нашем случае 250 мкс = 0.25мс
                              //Nдвигателя – число шагов двигателя на полный оборот; В нашем случае 200 шагов/оборот
                              //Nмикрошагов – число микро шагов двигателя на одну фазу. Для шагового режима Nмикрошагов = 1, для полу шагового Nмикрошагов = 2 и т.д
                              //в нашем случае Nмикрошагов = 2
  myMotor.step(0);  // начальный запуск
                    //Настроим подсветку дисплея
  pinMode(BACKLIGHT_PIN, OUTPUT);
  digitalWrite(BACKLIGHT_PIN, HIGH);
  //------------------записываем в EEPROM скорость шагового двигателя s в шагах за секунду
  //EEPROM.write(0,10); //1 шаг - 1.8 град т.е 180 гр. за секунду или 1 оборот за 2 секунды, что соответствует 30 об/мин
  //Считаем из постоянной памяти скорость s
  divider=EEPROM.read(0);
  //-----------------------------------------------------------------------------------------
   //Очистим дисплей
  lcd.begin(16, 2);
  MsTimer2::set(1,timerInterupt2); // задаем период прерывания по таймеру 2 мс
  MsTimer2::start();              // разрешаем прерывание по таймеру
 
  wdt_enable(WDTO_4S); // разрешение на работу сторожевого таймера с тайм-аутом 4с
}
//------------------------------------------------------------------------------------------------------
//-------------------------------------- обработчик прерывания 0,25 мс по первому таймеру
void  timerInterrupt()
 {
    myMotor.control(); // управвление двигателем
 }
 //----------------------------------------------------------------------------------------------     
                                                //Определим функцию для опроса аналоговой клавиатуры
            //Функция опроса клавиатуры, принимает адрес пина (в нашем случае А0), к которому подключена клавиатура, и возвращает код клавиши:
            // 1 - UP
            // 2 - DOWN
            // 3 - LEFT
            // 4 - RIGHT
            // 5 - SELECT

 int ReadKey()            // фунуция опроса аналоговой клавиатуры
        {
          //Serial.println();
          //keyCodeA=0;
          int KeyNumNow=0;
                           KeyValue2=analogRead(KEYPAD_PIN); // чтение кнопки
                           //Serial.print("KeyValue2  = ");Serial.println(KeyValue2); 
                  if (KeyValue2>=0&&KeyValue2 < 1000) // возможно какая-то кнопка нажата
                   {           
                     if (flagPressA==false)//если ни одна кнопка не была нажата (на предыдущем шаге все кнопки отжаты)
                      {
                            //Интерпретируем полученное значение и определяем код нажатой клавиши
                              if (KeyValue2<30) {KeyNumNow=4;}//Right
                         else if (KeyValue2<180) {KeyNumNow=1;}//Up
                         else if (KeyValue2<350) {KeyNumNow=2;}//Down
                         else if (KeyValue2<550) {KeyNumNow=3;}//Left
                         else if (KeyValue2<800) {KeyNumNow=5;}//Select
                             if (KeyNumPrev!=KeyNumNow)//если код кнопки на предыдущем шаге не совпадает с кодом кнопки на текущем шаге
                              {
                               buttonCount =0; //сброс счетчика повторяемости кода кнопки
                               KeyNumPrev = KeyNumNow; //для проверки повторяемости кодов кнопки (устойчивого ее состояния)
                              }
                             else buttonCount++; //счетчие на каждом шаге при совпадении кодов кнопки возрастает на 1
                             if (buttonCount >= SUM_BUTTON_COUNT)//критерий устойчивого состояния кнопки при ее нажатии)
                              {
                               keyCodeA = KeyNumNow;
                               buttonCount =0; //сброс счетчика если принято решение об устойчивом состоянии нажатой кнопки
                               flagPressA=true;    //состояние аналогового входа А0 - какая то кнопка нажата
                               flagClickA=true;   // Клик (нажатие) кнопки на аналоговом входе А0 с номером keyCodeA
                               
                              }
                    } //конец проверки, что кнопка нажата.               
                } else {flagPressA = false; keyCodeA = 0;}//В противном случае выходим из программы с 0 кодом кнопки
        Serial.print("                keyCodeA  = ");Serial.println(keyCodeA);           
        return keyCodeA; //Возвращаем код нажатой клавиши         
        }
//------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------------
            //Определим процедуру редактирования уставки делителя частоты divider для управления скоростью вращения ШД
            //Вызывается по нажатию клавиши Select с номером нажатия numKey5==1.
            //Отображает на дисплее уставку divider для изменения скорости вращения двигателя (ШД)  позволяет изменять ее клавишами Up и Down
            void  set_Divider()
 {
            //Опрашиваем клавиатуру, если нажата клавиша Up увеличиваем значение на 1, если Down – уменьшаем на 1
            //Если нажаты клавиши Left или Right – цикл опроса прерывается
           
                                                                                                                                 
                                             
                                              if (flagClickA==true&&keyCodeA==1)
                                               {
                                                flagClickA=false; tone(SOUND_PIN,FREQ,T_BUZ);
                                                divider++;
                                               }
                                              if (flagClickA==true&&keyCodeA==2)
                                               {
                                                flagClickA=false; tone(SOUND_PIN,FREQ,T_BUZ);
                                                divider--;
                                               }
                                              /* if (abs(millis()-lastouttime)>T_ind) //выводим на экран раз в секунду (для уменьшения мерцания)
                                                {     
                                                    lcd.clear();
                                                    lcd.setCursor(0, 0);
                                              lcd.print("S:");
                                              lcd.setCursor(0,1); 
                                              lcd.print(s);
                                               lastouttime=millis();
                                                }*/
                                           
            //По клавише Left – сохраняем в EEPROM измененное значение
            //По клавише Right – восстанавливаем старое значение
                      if (flagClickA==true&&keyCodeA==3)
                       {
                        flagClickA=false; tone(SOUND_PIN,FREQ,T_BUZ);
                        EEPROM.write(0, divider);
                       }
                      if (flagClickA==true&&keyCodeA==4)
                       {
                        flagClickA=false; tone(SOUND_PIN,FREQ,T_BUZ);
                        divider=EEPROM.read(0);
                       }
                       
  }     
//-----------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------
            //определяем процедуру вывода на дисплей текущих параметров
            void ViewPar()
 {
   if (gorelka4.flagPress == false)//если горелка еше не включина
                                   {
                                 lcd.clear();
                        lcd.setCursor(0, 0);
                        lcd.print("divider  r/min");
                        lcd.setCursor(0, 1);
                        lcd.print(divider);
                        lcd.setCursor(7, 1);
                        lcd.print(r_min);
                        lcd.setCursor(13, 1);
                        lcd.print("---");
                                   }
                                  if (gorelka4.flagPress == true)//если горелка включилась
                                   {
                                        lcd.clear();
                        lcd.setCursor(0, 0);
                        lcd.print("divider r/min"); //разрешения включения горелки уже есть
                        lcd.setCursor(0, 1);
                        lcd.print(divider);
                        lcd.setCursor(7, 1);
                        lcd.print(r_min);
                        lcd.setCursor(13, 1);
                        lcd.print("+++");
                                   }
   
 }
 //-------------------------------------------------------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------------------       
                    //функция прерывания по таймеру
                               void  timerInterupt2()
 {
 
  gorelka4.filterAvarage();  // вызов метода фильтрации по среднему для контактов реле, управляемого напряжением включения горелки
  //keyCodeA=ReadKey();
 /* if (gorelka4.flagClick == true) // если замыкаются контакты реле, управляемого напряжением включения горелки
              {
                StartWorkGorelka = millis(); //время старта работы горелки в миллисекундах
                prevStatePress4 = true; //установка признака замкнутых контактов реле управляемого напряжением включения горелки (синими проводами)
                gorelka4.flagClick = false; //сброс признака замыкания контактов реле, управляемого напряжением включения горелки
              }
  if (gorelka4.flagPress == false && prevStatePress4 == true) //контакты реле, управляемого (синими проводами) включением горелки, разомкнулись (на пине 4 стало высокое),а предыдущее состояние контактов - замкнуты
              {
               prevStatePress4 = false; //сброс признака замыкания контактов реле управляемого напряжением включения горелки (синими проводами)
              } */
              if (gorelka4.flagPress == false) //если горелка выключена, то ШД остановлен и можно изменять частоту вращения
               {
                                  myMotor.step(0);//останов двигателя
                                   keyCodeA=ReadKey(); //функция чтения аналоговой клавиатуры
 
                      if (flagClickA==true&&keyCodeA==5)//нажата кнопка СЕЛЕКТ
                       {
                        numKey5=1; //количество нажатий кнопки селект
                        lastT_keyCodeA_5 = millis(); //фиксируем время нажатия клавиши СЕЛЕКТ
                        flagClickA=false; tone(SOUND_PIN,FREQ,T_BUZ);
                       }
                     if (abs(millis()-lastT_keyCodeA_5) < maxT_keyCodeA_5 && numKey5==1) //заданный интервал времени коррекции уставок (превышение времени приведет к выходу из коррекции)
                      {
                       set_Divider(); //корректируем делитель частоты
                      } else numKey5=0; //количество нажатий кнопки селект
               }
              if (gorelka4.flagPress == true) //включилась горелка и контакты реле, управляемого синими проводами горелки замкнулись (на пине 4 стало низкое)
               {
                myMotor.setDivider(divider);
                myMotor.step(80000);//запуск двигателя на большое число шагов для непрерывного вращения
               } 
   wdt_reset();  // сброс сторожевого таймера (если контроллер зависнет, то сброса сторожевого таймера не будет и произойдет перезагрузка)
 }       
//------------------------------------------------------------------------------------------------------
void loop()
{
  r_min = 600/divider; //скорость вращения ШД в об/мин
   if (abs(millis()-lastouttime)>T_ind) //выводим на экран раз в секунду (для уменьшения мерцания)
    {     
     ViewPar();   
     lastouttime=millis();
    }
   
}


Вернуться в «Шаговые двигатели и драйверы»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость