Шаговый двигатель (оптимизация)

NEVEC
Offline
Зарегистрирован: 25.09.2017

Добрый день! Сделал на базе ардуино и шагового двигателя (28BYJ-48) управления вертикальным жалюзи.

Есть скетч, но есть 3 момента:

1) Можно ли этот код оптимизировать?

2) Не нашел как отключить фиксацию ротора шагового двигателя, движок грееться и расходует лишнюю энергию.

3) Подключить дополнительно дублирующие щаговые двигатели (сколько можно их подключить?) В идеале хочу добиться управления 6ти вертикальных жалюжи, 3(независимое упр)+3 (независимое упр).

/*Программа для шагового двигателя 28BYJ-48 (5V). В схеме есть кнопка и потенциометр. В зависимости от положения кнопки (пол. 1, пол. 2, выключено) мотор вращается либо в одну сторону, либо в другую, либо стоит на месте, а потенциометр влияет на скорость вращения.*/
/*У данного мотора 4 провода (оранж., жёлт., розов., син.), которые мы подключаем к контактам ардуино. Номера контактов указываем в массиве MotorPins, в порядке, соответствующем перечислению цветов, в нашем случае с D12 по D9*/
int MotorPins[4] = {8, 9, 10, 11};

/*Контакты от двух положений кнопки - цифровые*/
const int ButtonOn1 = 5;
const int ButtonOn2 = 4;

/*Контакт регистрирующий значение потенциометра - аналоговый*/
const int PotenciomData = 0;

/*Целочисленная константа, показывающая количество фаз подачи сигналов для одного шага мотора. Для полушагового режима - 8
Для шагового - 4*/
const int OneTurnPhasesCount = 8;

/*Целочисленная переменная, показывающая задержку в миллисекундах между фазами подачи сигналов мотору. Для полушагового режима - 2,
для шагового - 3*/
int TurnPhasesDelay = 2;

/*Целочисленная переменная, показывающая номер текущей фазы*/
int CurrentPhase = 0;

//состояние кнопки включено-выключено
int ButtonState = 0;

/*Целочисленная переменная, показывающая направление вращения мотора: 1 - по часовой стрелке, 0 - против*/
int TurnDirection = 1;

/*целочисленная константа, показывающая временную задержку между считыванием состояния кнопки и потенциометра*/
const int CheckButtonDelay = 15;

/*Целочисленная переменная показывающая, сколько прошло времени и не пора ли считывать состояние кнопки*/
int CurrentButtonDelay = 0;

//Для полушагового режима

/*Массив, в котором указано какие сигналы подавать на контакты мотора в той или иной фазе. [фаза][контакт]. Контакты даются в порядке, перечисленном в массиве MotorPins - оранж., жёлт., розов., син. 0 - нет сигнала, 1 - есть сигнал*/
bool MotorTurnPhases[8][4] = {
  { 1, 1, 0, 0},
  { 0, 1, 0, 0},
  { 0, 1, 1, 0},
  { 0, 0, 1, 0},
  { 0, 0, 1, 1},
  { 0, 0, 0, 1},
  { 1, 0, 0, 1},
  { 1, 0, 0, 0} };

/*Функция, в которой происходит инициализация всех переменных программы*/
void setup()
{
/*перебираем в цикле все контакты массива MotorPins и присваиваем им значение выходных, то есть дающих напряжение в 5В*/
  for (int i = 0; i < 4; i++)
    pinMode(MotorPins[i], OUTPUT);

  pinMode(ButtonOn1, INPUT);
  pinMode(ButtonOn2, INPUT);
  pinMode(PotenciomData, INPUT);
}

/*Функция-цикл в которой задаётся поведение программы*/
void loop()
{
  if(CurrentButtonDelay >= CheckButtonDelay)
  {
    CheckButtonState();
    CurrentButtonDelay = 0;
  }

  if(ButtonState != 0)
  {
//проверяем индекс текущей фазы
    CheckLastPhase();

/*подаём напряжения на контакты мотора соответственно фазе, заданной в массиве MotorTurnPhases*/
    for (int i = 0; i < 4; i++)
    {
      digitalWrite(MotorPins[i], ( (MotorTurnPhases[CurrentPhase][i] == 1) ? HIGH : LOW) );
    }

//переходим к другой фазе
    CurrentPhase += TurnDirection;

// Пауза между фазами
    delay(TurnPhasesDelay);
  }

  CurrentButtonDelay += TurnPhasesDelay;
}

/*Функция CheckLastPhase проверяет не вышел ли номер текущей фазы за пределы размера массива MotorTurnPhases, который определяется переменной OneTurnPhasesCount*/
void CheckLastPhase()
{
  if (CurrentPhase >= OneTurnPhasesCount)
  {
    CurrentPhase = 0;
  }
  if (CurrentPhase < 0)
  {
    CurrentPhase = (OneTurnPhasesCount-1);
  }
}

/*функция, в которой проверяется текущее состояние кнопки*/
void CheckButtonState()
{
  int CurrentButtonState = 0, CurrentButtonDirection = 0, CurrentTurnPhasesDelay = 0;

//считываем данные с положения кнопки I
  bool readbuttonparam = digitalRead(ButtonOn1);

  if(readbuttonparam)
  {
    CurrentButtonState = 1;
    CurrentButtonDirection = 1;
  }

//считываем данные с положения кнопки II
  readbuttonparam = digitalRead(ButtonOn2);

  if(readbuttonparam)
  {
    CurrentButtonState = 1;
    CurrentButtonDirection = -1;
  }

/*Проверяем, изменилось ли состояние кнопки по сравнению с предыдущим, и если изменилось, то записываем изменения в глобальные переменные*/
  if(ButtonState != CurrentButtonState)
  {
    ButtonState = CurrentButtonState;
  }

  if(TurnDirection != CurrentButtonDirection)
  {
    TurnDirection = CurrentButtonDirection;
  }

  CurrentTurnPhasesDelay = map(analogRead(PotenciomData), 0, 1023, 2, CheckButtonDelay);

  if(TurnPhasesDelay != CurrentTurnPhasesDelay)
  {
    TurnPhasesDelay = CurrentTurnPhasesDelay;
  }
}

 

NEVEC
Offline
Зарегистрирован: 25.09.2017

Прошу помочь

(Шаговый двигатель 28BYJ-84, драйвер на основе ULN2003)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

NEVEC пишет:

1) Можно ли этот код оптимизировать?

Оптимизировать можно всё. Другой вопрос, что Вы не указали цель оптимизации.

А код подробно не глядел, но на первый взгляд выглядит прилично. Стесняюсь спросить ... сами писали?

NEVEC
Offline
Зарегистрирован: 25.09.2017

Брал отсюда http://www.techclub.su/article_arduino06. Цель оптимизации минимальное энергопотребление! Что бы включил в розетку и забыл.

В данный момент при остановке шаговик фиксирует себя и наченает греться, не могу понять как отключить эту функцию.

Так же могу ли я скажем так парралельно добавить шаговый двигатель после драйвера или через ардуионо?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Движок, как правило, отключается подаванием одинаковых уровней на драйвер мотора, читайте доку к своему драйверу. Например, подав HIGH на оба управляющих входа H-моста - можно отключить мотор, при этом ротор будет вращаться руками свободно. Короче - всё зависит от драйвера, смотрите доку по нему.

Onkel
Offline
Зарегистрирован: 22.02.2016

Нет, это другой драйвер - для 4 ф униполярного шаговика. Там 4 входа и 4 выхода. Дал High- выход на землю (open collector), дал Low- выход болтается. Это просто четверной дарлингтон 2003.

Onkel
Offline
Зарегистрирован: 22.02.2016

Только с двумя драйверами. Для отключения шд во время паузы подайте LOW на все пины управления драйвером ШД

NEVEC
Offline
Зарегистрирован: 25.09.2017

/*Программа для шагового двигателя 28BYJ-48 (5V). В схеме есть кнопка и потенциометр. В зависимости от положения кнопки (пол. 1, пол. 2, выключено) мотор вращается либо в одну сторону, либо в другую, либо стоит на месте, а потенциометр влияет на скорость вращения.*/
/*У данного мотора 4 провода (оранж., жёлт., розов., син.), которые мы подключаем к контактам ардуино. Номера контактов указываем в массиве MotorPins, в порядке, соответствующем перечислению цветов, в нашем случае с D12 по D9*/
int MotorPins[4] = {8, 9, 10, 11};

/*Контакты от двух положений кнопки - цифровые*/
const int ButtonOn1 = 5;
const int ButtonOn2 = 4;

/*Контакт регистрирующий значение потенциометра - аналоговый*/
const int PotenciomData = 0;

/*Целочисленная константа, показывающая количество фаз подачи сигналов для одного шага мотора. Для полушагового режима - 8
Для шагового - 4*/
const int OneTurnPhasesCount = 8;

/*Целочисленная переменная, показывающая задержку в миллисекундах между фазами подачи сигналов мотору. Для полушагового режима - 2,
для шагового - 3*/
int TurnPhasesDelay = 2;

/*Целочисленная переменная, показывающая номер текущей фазы*/
int CurrentPhase = 0;

//состояние кнопки включено-выключено
int ButtonState = 0;

/*Целочисленная переменная, показывающая направление вращения мотора: 1 - по часовой стрелке, 0 - против*/
int TurnDirection = 1;

/*целочисленная константа, показывающая временную задержку между считыванием состояния кнопки и потенциометра*/
const int CheckButtonDelay = 15;

/*Целочисленная переменная показывающая, сколько прошло времени и не пора ли считывать состояние кнопки*/
int CurrentButtonDelay = 0;

//Для полушагового режима

/*Массив, в котором указано какие сигналы подавать на контакты мотора в той или иной фазе. [фаза][контакт]. Контакты даются в порядке, перечисленном в массиве MotorPins - оранж., жёлт., розов., син. 0 - нет сигнала, 1 - есть сигнал*/
bool MotorTurnPhases[8][4] = {
  { 1, 1, 0, 0},
  { 0, 1, 0, 0},
  { 0, 1, 1, 0},
  { 0, 0, 1, 0},
  { 0, 0, 1, 1},
  { 0, 0, 0, 1},
  { 1, 0, 0, 1},
  { 1, 0, 0, 0} };

/*Функция, в которой происходит инициализация всех переменных программы*/
void setup()
{
/*перебираем в цикле все контакты массива MotorPins и присваиваем им значение выходных, то есть дающих напряжение в 5В*/
  for (int i = 0; i < 4; i++)
    pinMode(MotorPins[i], OUTPUT);

  pinMode(ButtonOn1, INPUT);
  pinMode(ButtonOn2, INPUT);
  pinMode(PotenciomData, INPUT);
}

/*Функция-цикл в которой задаётся поведение программы*/
void loop()
{
  if(CurrentButtonDelay >= CheckButtonDelay)
  {
    CheckButtonState();
    CurrentButtonDelay = 0;
  }

  if(ButtonState != 0)
  {
//проверяем индекс текущей фазы
    CheckLastPhase();

/*подаём напряжения на контакты мотора соответственно фазе, заданной в массиве MotorTurnPhases*/
    for (int i = 0; i < 4; i++)
    {
      digitalWrite(MotorPins[LOW], ( (MotorTurnPhases[CurrentPhase][i] == 1) ? HIGH : LOW) );
    }

//переходим к другой фазе
    CurrentPhase += TurnDirection;

// Пауза между фазами
    delay(TurnPhasesDelay);

    
  }

  CurrentButtonDelay += TurnPhasesDelay;
}

/*Функция CheckLastPhase проверяет не вышел ли номер текущей фазы за пределы размера массива MotorTurnPhases, который определяется переменной OneTurnPhasesCount*/
void CheckLastPhase()
{
  if (CurrentPhase >= OneTurnPhasesCount)
  {
    CurrentPhase = 0;
  }
  if (CurrentPhase < 0)
  {
    CurrentPhase = (OneTurnPhasesCount-1);
  }
}

/*функция, в которой проверяется текущее состояние кнопки*/
void CheckButtonState()
{
  int CurrentButtonState = 0, CurrentButtonDirection = 0, CurrentTurnPhasesDelay = 0;

//считываем данные с положения кнопки I
  bool readbuttonparam = digitalRead(ButtonOn1);

  if(readbuttonparam)
  {
    CurrentButtonState = 1;
    CurrentButtonDirection = 1;
  }

//считываем данные с положения кнопки II
  readbuttonparam = digitalRead(ButtonOn2);

  if(readbuttonparam)
  {
    CurrentButtonState = 1;
    CurrentButtonDirection = -1;
  }

/*Проверяем, изменилось ли состояние кнопки по сравнению с предыдущим, и если изменилось, то записываем изменения в глобальные переменные*/
  if(ButtonState != CurrentButtonState)
  {
    ButtonState = CurrentButtonState;
  }

  if(TurnDirection != CurrentButtonDirection)
  {
    TurnDirection = CurrentButtonDirection;
  }

  CurrentTurnPhasesDelay = map(analogRead(PotenciomData), 0, 1023, 2, CheckButtonDelay);

  if(TurnPhasesDelay != CurrentTurnPhasesDelay)
  {
    TurnPhasesDelay = CurrentTurnPhasesDelay;
  }
}

Просто моргает 1 светодиод на драйвере(

Onkel
Offline
Зарегистрирован: 22.02.2016

после

069
	  if(ButtonState != 0)
070
	  {
071
	//проверяем индекс текущей фазы
072
	    CheckLastPhase();
073
	 
074
	/*подаём напряжения на контакты мотора соответственно фазе, заданной в массиве MotorTurnPhases*/
075
	    for (int i = 0; i < 4; i++)
076
	    {
077
	      digitalWrite(MotorPins[LOW], ( (MotorTurnPhases[CurrentPhase][i] == 1) ? HIGH : LOW) );
078
	    }
079
	 
080
	//переходим к другой фазе
081
	    CurrentPhase += TurnDirection;
082
	 
083
	// Пауза между фазами
084
	    delay(TurnPhasesDelay);
085
	 
086
	     
087
	  }

 

 

напишите

 

else
{
digitalOut(9,LOW);
digitalOut(10,LOW);
digitalOut(11,LOW);
digitalOut(12,LOW);

}

 

GarryC
Offline
Зарегистрирован: 08.08.2016

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

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

Onkel
Offline
Зарегистрирован: 22.02.2016

ButtonState и так уже очищена от дребезга, разве нет?

и задержка уже есть есть        

delay(TurnPhasesDelay);

 

sergkr1983
Offline
Зарегистрирован: 09.12.2015

Можно (как вариант) в разрез на драйвер сборку реле 4х канальное. При включении реле замыкает провод. Отработал шаговик - вырубить реле.

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014

Вот когдато писал для себя, может и Вам подойдет

//**********  (РЕЖИМ 1-ШАГ) Крутим с помощью сдвига --рабочая - крутит туда сюда без потери шагов - ускоряет - отключает катушки после бездействия
//PORTB - 8, 9, 10, 11 - Шаговик. && 12, 13 - Вход сигнал от 2х кнопок (влево - вправо)

#define uskor 5 //Задаем ускорение
byte n1 = 1;//  - Сдвигаем 1 бит;
long int perem_X = 0;//Задаём количество шагов и направление
byte f_tim = 0;
//byte varB_X = 0;
//byte varM_X = 0;

byte sped_Xt = uskor;//Начальная скорость
byte sped_X = 0; //Ускорение
bool f_run1 = 0;//Сброс после остановки 2 переменные.
bool f_run2 = 0;
//bool f_run3 = 0;
unsigned long no_run; //Переменная для прошедшего периода

void setup() {
  //  пин, 8, 9, 10, 11 - Шаговик, как выход. 12, 13-кнопки, вход
  DDRB = 0xCF;
  //Устанавлюем значение выходов в 0 а входы - подтягивающий резистор
  PORTB = 0x30;
  //Serial.begin(9600);

  // Инициализировать TIMER1
  noInterrupts (); // отключить все прерывания
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  OCR1A = 125; // сравнить регистр (скорость!!!(2ms=125 - если медлиннее - УВЕЛИЧУЕМ)
  TCCR1B |= (1 << WGM12); // Режим СТС
  TCCR1B |= (1 << CS12); // 256 делитель
  TIMSK1 |= (1 << OCIE1A ); // включить таймер сравнить прерываний
  interrupts(); // включить все прерывания
}

ISR (TIMER1_COMPA_vect) // Функция прерывания таймера
{
  // Serial.println(f_tim);
  f_tim = 1;
}

//Функция вращения 1 dvigatel
void RunX_up ()
{
  f_tim = 0;
  if (perem_X > 0) {
    n1 = (n1 >> 1) | (n1 << 3); //Сдвигаем
    --perem_X;
  }
  else {                          //if (perem_X < 0)
    n1 = (n1 << 1) | (n1 >> 3); //Сдвигаем
    ++perem_X;
  }
  n1 = 0x0F & n1; //Маскируем кнопки
  PORTB = (PORTB & 0xF0) | n1; //Очищаем Младшие биты и пишем туда n1
}

void loop() {

  //Кнопки 1 и 2 служат для демонстрации работы. Считается замкнутой при подаче МИНУСА
  if (!(PINB & (1 << 4))) //проверяем кнопку 1 - нажали? Можно использовать if(!digitalRead(12))
  {
    //Задаем количество шагов для дв.- ВПРАВО "perem_X = Количеству шагов;"
    //Сейчас стоит - держим кнопку крутится отпустили СТОП
    perem_X = 1;
  }
  else perem_X = 0; // ВНИМАНИЕ если задаём количество шагов - эту строчку удаляем!!!

  if (!(PINB & (1 << 5))) //проверяем кнопку 2 - нажали? Можно использовать if(!digitalRead(13))
  {
    //Задаем количество шагов для дв.- ВЛЕВО "perem_X = -Количеству шагов;"
    //Сейчас стоит - держим кнопку крутится отпустили СТОП
    perem_X = -1;
  }

  /////////////////////////// Запуск 1го двигателя если " perem_X != 0 "
  if (perem_X != 0) {
    f_run1 = 1;
    f_run2 = 0;

    if (f_tim) {
      if (!sped_X) { //Закончилось ли ускорение
        RunX_up ();
        if (sped_Xt > 0) {
          --sped_Xt;
          sped_X = sped_Xt;
        }
      }
      else {        //ускорение Закончилось
        --sped_X;
        //Serial.println(sped_X);
        f_tim = 0;
      }
    }
  }
  else {
    sped_Xt = uskor;
    sped_X = 0; // для пропуска 1го тика
  }

  if (f_run1 && perem_X == 0) //Служит для запуска отключения катушек
  {
    f_run1 = 0;
    f_run2 = 1;
    no_run = millis();
  }

  ////////////////////////////////
  if (f_run2 && millis() - no_run >= 1000)// Проверяем закончилось ли время
  {
    f_run2 = 0;
    //Снимаем напряжение с катушек
    PORTB &= 0xF0;
  }
}

 

NEVEC
Offline
Зарегистрирован: 25.09.2017

Увы для меня не подходят!

Нашел в просторах ютуба интересный ролик https://www.youtube.com/watch?v=FSIaVWwrMx4&t=147s

https://www.youtube.com/watch?v=B36QTZdG8zs&feature=youtu.be

https://www.youtube.com/watch?v=FSIaVWwrMx4&feature=youtu.be

Вот скетч:

// программа следящего электропривода без обратной связи 

#include <TimerOne.h> 
#include <StepMotor.h> 

#define MEASURE_PERIOD 480 // время периода измерения (* 250 мкс) 
#define numStepsMotor 1900 // число шагов двигателя на оборот 

int timeCount; // счетчик времени 
long sumU; // переменные для суммирования кодов АЦП 
long averageU; // сумма кодов АЦП (среднее значение * 80) 
int currentStep; // текущее положение двигателя 
int setStep; // заданное положение двигателя 

StepMotor myMotor(8, 9, 10, 11); // создаем объект типа StepMotor, задаем выводы для фаз 

void setup() { 
Timer1.initialize(250); // инициализация таймера 1, период 250 мкс 
Timer1.attachInterrupt(timerInterrupt, 250); // обработчик прерываний 
myMotor.setMode(0, false); // шаговый режим, без фиксации при остановке 
myMotor.setDivider(15); // делитель частоты 15 
} 

void loop() { 
// проверка остановки двигателя 
if( myMotor.readSteps() == 0) { 
// двигатель остановился 

// вычисление заданного положения 
setStep = averageU * (numStepsMotor - 1) / 1023 / MEASURE_PERIOD; 

// определение сколько шагов надо сделать 
int stepsToDo; // сколько шагов надо сделать 

stepsToDo = currentStep - setStep; // ошибка рассогласования 

if( abs(stepsToDo) >= (numStepsMotor / 1) ) { 

if((stepsToDo) > 0) stepsToDo -= numStepsMotor; 
else stepsToDo += numStepsMotor; 
} 

myMotor.step(stepsToDo); // запуск двигателя 
currentStep = setStep; // перегрузка текущего положения 
} 
} 

//------------------------------------— обработчик прерывания 250 мкс 
void timerInterrupt() { 
myMotor.control(); // управвление двигателем 

sumU += analogRead(A0); // суммирование кодов АЦП 
timeCount++; // +1 счетчик выборок усреднения 

// проверка числа выборок усреднения 
if ( timeCount >= MEASURE_PERIOD ) { 
timeCount= 0; 
averageU= sumU; // перегрузка среднего значения 
sumU= 0; 
} 
} 

полностью рабочая (проверял) с возможностью переключеня в авто режим (фототранзистор).

Но есть недостаток если потенциометр например в положении 50% (0-100%) при отключении и вновь подачи питания шаговый двигатель прокручивается на определенный шаг сбивается калибровка (т.е может сломать жалюжи). Можно ли доработать данную схему. Ну конечно не бесплатно! 

RuslanX
RuslanX аватар
Offline
Зарегистрирован: 20.05.2017

NEVEC пишет:

Вот скетч:

// программа следящего электропривода без обратной связи 

#include <StepMotor.h> 

NEVEC здравствуй. А библиотеку можно от вас получить. Я так понял ее отдельно писали.