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

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

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

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

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

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

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

001/*Программа для шагового двигателя 28BYJ-48 (5V). В схеме есть кнопка и потенциометр. В зависимости от положения кнопки (пол. 1, пол. 2, выключено) мотор вращается либо в одну сторону, либо в другую, либо стоит на месте, а потенциометр влияет на скорость вращения.*/
002/*У данного мотора 4 провода (оранж., жёлт., розов., син.), которые мы подключаем к контактам ардуино. Номера контактов указываем в массиве MotorPins, в порядке, соответствующем перечислению цветов, в нашем случае с D12 по D9*/
003int MotorPins[4] = {8, 9, 10, 11};
004 
005/*Контакты от двух положений кнопки - цифровые*/
006const int ButtonOn1 = 5;
007const int ButtonOn2 = 4;
008 
009/*Контакт регистрирующий значение потенциометра - аналоговый*/
010const int PotenciomData = 0;
011 
012/*Целочисленная константа, показывающая количество фаз подачи сигналов для одного шага мотора. Для полушагового режима - 8
013Для шагового - 4*/
014const int OneTurnPhasesCount = 8;
015 
016/*Целочисленная переменная, показывающая задержку в миллисекундах между фазами подачи сигналов мотору. Для полушагового режима - 2,
017для шагового - 3*/
018int TurnPhasesDelay = 2;
019 
020/*Целочисленная переменная, показывающая номер текущей фазы*/
021int CurrentPhase = 0;
022 
023//состояние кнопки включено-выключено
024int ButtonState = 0;
025 
026/*Целочисленная переменная, показывающая направление вращения мотора: 1 - по часовой стрелке, 0 - против*/
027int TurnDirection = 1;
028 
029/*целочисленная константа, показывающая временную задержку между считыванием состояния кнопки и потенциометра*/
030const int CheckButtonDelay = 15;
031 
032/*Целочисленная переменная показывающая, сколько прошло времени и не пора ли считывать состояние кнопки*/
033int CurrentButtonDelay = 0;
034 
035//Для полушагового режима
036 
037/*Массив, в котором указано какие сигналы подавать на контакты мотора в той или иной фазе. [фаза][контакт]. Контакты даются в порядке, перечисленном в массиве MotorPins - оранж., жёлт., розов., син. 0 - нет сигнала, 1 - есть сигнал*/
038bool MotorTurnPhases[8][4] = {
039  { 1, 1, 0, 0},
040  { 0, 1, 0, 0},
041  { 0, 1, 1, 0},
042  { 0, 0, 1, 0},
043  { 0, 0, 1, 1},
044  { 0, 0, 0, 1},
045  { 1, 0, 0, 1},
046  { 1, 0, 0, 0} };
047 
048/*Функция, в которой происходит инициализация всех переменных программы*/
049void setup()
050{
051/*перебираем в цикле все контакты массива MotorPins и присваиваем им значение выходных, то есть дающих напряжение в 5В*/
052  for (int i = 0; i < 4; i++)
053    pinMode(MotorPins[i], OUTPUT);
054 
055  pinMode(ButtonOn1, INPUT);
056  pinMode(ButtonOn2, INPUT);
057  pinMode(PotenciomData, INPUT);
058}
059 
060/*Функция-цикл в которой задаётся поведение программы*/
061void loop()
062{
063  if(CurrentButtonDelay >= CheckButtonDelay)
064  {
065    CheckButtonState();
066    CurrentButtonDelay = 0;
067  }
068 
069  if(ButtonState != 0)
070  {
071//проверяем индекс текущей фазы
072    CheckLastPhase();
073 
074/*подаём напряжения на контакты мотора соответственно фазе, заданной в массиве MotorTurnPhases*/
075    for (int i = 0; i < 4; i++)
076    {
077      digitalWrite(MotorPins[i], ( (MotorTurnPhases[CurrentPhase][i] == 1) ? HIGH : LOW) );
078    }
079 
080//переходим к другой фазе
081    CurrentPhase += TurnDirection;
082 
083// Пауза между фазами
084    delay(TurnPhasesDelay);
085  }
086 
087  CurrentButtonDelay += TurnPhasesDelay;
088}
089 
090/*Функция CheckLastPhase проверяет не вышел ли номер текущей фазы за пределы размера массива MotorTurnPhases, который определяется переменной OneTurnPhasesCount*/
091void CheckLastPhase()
092{
093  if (CurrentPhase >= OneTurnPhasesCount)
094  {
095    CurrentPhase = 0;
096  }
097  if (CurrentPhase < 0)
098  {
099    CurrentPhase = (OneTurnPhasesCount-1);
100  }
101}
102 
103/*функция, в которой проверяется текущее состояние кнопки*/
104void CheckButtonState()
105{
106  int CurrentButtonState = 0, CurrentButtonDirection = 0, CurrentTurnPhasesDelay = 0;
107 
108//считываем данные с положения кнопки I
109  bool readbuttonparam = digitalRead(ButtonOn1);
110 
111  if(readbuttonparam)
112  {
113    CurrentButtonState = 1;
114    CurrentButtonDirection = 1;
115  }
116 
117//считываем данные с положения кнопки II
118  readbuttonparam = digitalRead(ButtonOn2);
119 
120  if(readbuttonparam)
121  {
122    CurrentButtonState = 1;
123    CurrentButtonDirection = -1;
124  }
125 
126/*Проверяем, изменилось ли состояние кнопки по сравнению с предыдущим, и если изменилось, то записываем изменения в глобальные переменные*/
127  if(ButtonState != CurrentButtonState)
128  {
129    ButtonState = CurrentButtonState;
130  }
131 
132  if(TurnDirection != CurrentButtonDirection)
133  {
134    TurnDirection = CurrentButtonDirection;
135  }
136 
137  CurrentTurnPhasesDelay = map(analogRead(PotenciomData), 0, 1023, 2, CheckButtonDelay);
138 
139  if(TurnPhasesDelay != CurrentTurnPhasesDelay)
140  {
141    TurnPhasesDelay = CurrentTurnPhasesDelay;
142  }
143}

 

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
001/*Программа для шагового двигателя 28BYJ-48 (5V). В схеме есть кнопка и потенциометр. В зависимости от положения кнопки (пол. 1, пол. 2, выключено) мотор вращается либо в одну сторону, либо в другую, либо стоит на месте, а потенциометр влияет на скорость вращения.*/
002/*У данного мотора 4 провода (оранж., жёлт., розов., син.), которые мы подключаем к контактам ардуино. Номера контактов указываем в массиве MotorPins, в порядке, соответствующем перечислению цветов, в нашем случае с D12 по D9*/
003int MotorPins[4] = {8, 9, 10, 11};
004 
005/*Контакты от двух положений кнопки - цифровые*/
006const int ButtonOn1 = 5;
007const int ButtonOn2 = 4;
008 
009/*Контакт регистрирующий значение потенциометра - аналоговый*/
010const int PotenciomData = 0;
011 
012/*Целочисленная константа, показывающая количество фаз подачи сигналов для одного шага мотора. Для полушагового режима - 8
013Для шагового - 4*/
014const int OneTurnPhasesCount = 8;
015 
016/*Целочисленная переменная, показывающая задержку в миллисекундах между фазами подачи сигналов мотору. Для полушагового режима - 2,
017для шагового - 3*/
018int TurnPhasesDelay = 2;
019 
020/*Целочисленная переменная, показывающая номер текущей фазы*/
021int CurrentPhase = 0;
022 
023//состояние кнопки включено-выключено
024int ButtonState = 0;
025 
026/*Целочисленная переменная, показывающая направление вращения мотора: 1 - по часовой стрелке, 0 - против*/
027int TurnDirection = 1;
028 
029/*целочисленная константа, показывающая временную задержку между считыванием состояния кнопки и потенциометра*/
030const int CheckButtonDelay = 15;
031 
032/*Целочисленная переменная показывающая, сколько прошло времени и не пора ли считывать состояние кнопки*/
033int CurrentButtonDelay = 0;
034 
035//Для полушагового режима
036 
037/*Массив, в котором указано какие сигналы подавать на контакты мотора в той или иной фазе. [фаза][контакт]. Контакты даются в порядке, перечисленном в массиве MotorPins - оранж., жёлт., розов., син. 0 - нет сигнала, 1 - есть сигнал*/
038bool MotorTurnPhases[8][4] = {
039  { 1, 1, 0, 0},
040  { 0, 1, 0, 0},
041  { 0, 1, 1, 0},
042  { 0, 0, 1, 0},
043  { 0, 0, 1, 1},
044  { 0, 0, 0, 1},
045  { 1, 0, 0, 1},
046  { 1, 0, 0, 0} };
047 
048/*Функция, в которой происходит инициализация всех переменных программы*/
049void setup()
050{
051/*перебираем в цикле все контакты массива MotorPins и присваиваем им значение выходных, то есть дающих напряжение в 5В*/
052  for (int i = 0; i < 4; i++)
053    pinMode(MotorPins[i], OUTPUT);
054 
055  pinMode(ButtonOn1, INPUT);
056  pinMode(ButtonOn2, INPUT);
057  pinMode(PotenciomData, INPUT);
058}
059 
060/*Функция-цикл в которой задаётся поведение программы*/
061void loop()
062{
063  if(CurrentButtonDelay >= CheckButtonDelay)
064  {
065    CheckButtonState();
066    CurrentButtonDelay = 0;
067  }
068 
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  }
088 
089  CurrentButtonDelay += TurnPhasesDelay;
090}
091 
092/*Функция CheckLastPhase проверяет не вышел ли номер текущей фазы за пределы размера массива MotorTurnPhases, который определяется переменной OneTurnPhasesCount*/
093void CheckLastPhase()
094{
095  if (CurrentPhase >= OneTurnPhasesCount)
096  {
097    CurrentPhase = 0;
098  }
099  if (CurrentPhase < 0)
100  {
101    CurrentPhase = (OneTurnPhasesCount-1);
102  }
103}
104 
105/*функция, в которой проверяется текущее состояние кнопки*/
106void CheckButtonState()
107{
108  int CurrentButtonState = 0, CurrentButtonDirection = 0, CurrentTurnPhasesDelay = 0;
109 
110//считываем данные с положения кнопки I
111  bool readbuttonparam = digitalRead(ButtonOn1);
112 
113  if(readbuttonparam)
114  {
115    CurrentButtonState = 1;
116    CurrentButtonDirection = 1;
117  }
118 
119//считываем данные с положения кнопки II
120  readbuttonparam = digitalRead(ButtonOn2);
121 
122  if(readbuttonparam)
123  {
124    CurrentButtonState = 1;
125    CurrentButtonDirection = -1;
126  }
127 
128/*Проверяем, изменилось ли состояние кнопки по сравнению с предыдущим, и если изменилось, то записываем изменения в глобальные переменные*/
129  if(ButtonState != CurrentButtonState)
130  {
131    ButtonState = CurrentButtonState;
132  }
133 
134  if(TurnDirection != CurrentButtonDirection)
135  {
136    TurnDirection = CurrentButtonDirection;
137  }
138 
139  CurrentTurnPhasesDelay = map(analogRead(PotenciomData), 0, 1023, 2, CheckButtonDelay);
140 
141  if(TurnPhasesDelay != CurrentTurnPhasesDelay)
142  {
143    TurnPhasesDelay = CurrentTurnPhasesDelay;
144  }
145}

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

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

после

01069
02      if(ButtonState != 0)
03070
04      {
05071
06    //проверяем индекс текущей фазы
07072
08        CheckLastPhase();
09073
10      
11074
12    /*подаём напряжения на контакты мотора соответственно фазе, заданной в массиве MotorTurnPhases*/
13075
14        for (int i = 0; i < 4; i++)
15076
16        {
17077
18          digitalWrite(MotorPins[LOW], ( (MotorTurnPhases[CurrentPhase][i] == 1) ? HIGH : LOW) );
19078
20        }
21079
22      
23080
24    //переходим к другой фазе
25081
26        CurrentPhase += TurnDirection;
27082
28      
29083
30    // Пауза между фазами
31084
32        delay(TurnPhasesDelay);
33085
34      
35086
36          
37087
38      }

 

 

напишите

 

1else
2{
3digitalOut(9,LOW);
4digitalOut(10,LOW);
5digitalOut(11,LOW);
6digitalOut(12,LOW);
7 
8}

 

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

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

001//**********  (РЕЖИМ 1-ШАГ) Крутим с помощью сдвига --рабочая - крутит туда сюда без потери шагов - ускоряет - отключает катушки после бездействия
002//PORTB - 8, 9, 10, 11 - Шаговик. && 12, 13 - Вход сигнал от 2х кнопок (влево - вправо)
003 
004#define uskor 5 //Задаем ускорение
005byte n1 = 1;//  - Сдвигаем 1 бит;
006long int perem_X = 0;//Задаём количество шагов и направление
007byte f_tim = 0;
008//byte varB_X = 0;
009//byte varM_X = 0;
010 
011byte sped_Xt = uskor;//Начальная скорость
012byte sped_X = 0; //Ускорение
013bool f_run1 = 0;//Сброс после остановки 2 переменные.
014bool f_run2 = 0;
015//bool f_run3 = 0;
016unsigned long no_run; //Переменная для прошедшего периода
017 
018void setup() {
019  //  пин, 8, 9, 10, 11 - Шаговик, как выход. 12, 13-кнопки, вход
020  DDRB = 0xCF;
021  //Устанавлюем значение выходов в 0 а входы - подтягивающий резистор
022  PORTB = 0x30;
023  //Serial.begin(9600);
024 
025  // Инициализировать TIMER1
026  noInterrupts (); // отключить все прерывания
027  TCCR1A = 0;
028  TCCR1B = 0;
029  TCNT1 = 0;
030  OCR1A = 125; // сравнить регистр (скорость!!!(2ms=125 - если медлиннее - УВЕЛИЧУЕМ)
031  TCCR1B |= (1 << WGM12); // Режим СТС
032  TCCR1B |= (1 << CS12); // 256 делитель
033  TIMSK1 |= (1 << OCIE1A ); // включить таймер сравнить прерываний
034  interrupts(); // включить все прерывания
035}
036 
037ISR (TIMER1_COMPA_vect) // Функция прерывания таймера
038{
039  // Serial.println(f_tim);
040  f_tim = 1;
041}
042 
043//Функция вращения 1 dvigatel
044void RunX_up ()
045{
046  f_tim = 0;
047  if (perem_X > 0) {
048    n1 = (n1 >> 1) | (n1 << 3); //Сдвигаем
049    --perem_X;
050  }
051  else {                          //if (perem_X < 0)
052    n1 = (n1 << 1) | (n1 >> 3); //Сдвигаем
053    ++perem_X;
054  }
055  n1 = 0x0F & n1; //Маскируем кнопки
056  PORTB = (PORTB & 0xF0) | n1; //Очищаем Младшие биты и пишем туда n1
057}
058 
059void loop() {
060 
061  //Кнопки 1 и 2 служат для демонстрации работы. Считается замкнутой при подаче МИНУСА
062  if (!(PINB & (1 << 4))) //проверяем кнопку 1 - нажали? Можно использовать if(!digitalRead(12))
063  {
064    //Задаем количество шагов для дв.- ВПРАВО "perem_X = Количеству шагов;"
065    //Сейчас стоит - держим кнопку крутится отпустили СТОП
066    perem_X = 1;
067  }
068  else perem_X = 0; // ВНИМАНИЕ если задаём количество шагов - эту строчку удаляем!!!
069 
070  if (!(PINB & (1 << 5))) //проверяем кнопку 2 - нажали? Можно использовать if(!digitalRead(13))
071  {
072    //Задаем количество шагов для дв.- ВЛЕВО "perem_X = -Количеству шагов;"
073    //Сейчас стоит - держим кнопку крутится отпустили СТОП
074    perem_X = -1;
075  }
076 
077  /////////////////////////// Запуск 1го двигателя если " perem_X != 0 "
078  if (perem_X != 0) {
079    f_run1 = 1;
080    f_run2 = 0;
081 
082    if (f_tim) {
083      if (!sped_X) { //Закончилось ли ускорение
084        RunX_up ();
085        if (sped_Xt > 0) {
086          --sped_Xt;
087          sped_X = sped_Xt;
088        }
089      }
090      else {        //ускорение Закончилось
091        --sped_X;
092        //Serial.println(sped_X);
093        f_tim = 0;
094      }
095    }
096  }
097  else {
098    sped_Xt = uskor;
099    sped_X = 0; // для пропуска 1го тика
100  }
101 
102  if (f_run1 && perem_X == 0) //Служит для запуска отключения катушек
103  {
104    f_run1 = 0;
105    f_run2 = 1;
106    no_run = millis();
107  }
108 
109  ////////////////////////////////
110  if (f_run2 && millis() - no_run >= 1000)// Проверяем закончилось ли время
111  {
112    f_run2 = 0;
113    //Снимаем напряжение с катушек
114    PORTB &= 0xF0;
115  }
116}

 

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

Вот скетч:

01// программа следящего электропривода без обратной связи
02 
03#include <TimerOne.h>
04#include <StepMotor.h>
05 
06#define MEASURE_PERIOD 480 // время периода измерения (* 250 мкс)
07#define numStepsMotor 1900 // число шагов двигателя на оборот
08 
09int timeCount; // счетчик времени
10long sumU; // переменные для суммирования кодов АЦП
11long averageU; // сумма кодов АЦП (среднее значение * 80)
12int currentStep; // текущее положение двигателя
13int setStep; // заданное положение двигателя
14 
15StepMotor myMotor(8, 9, 10, 11); // создаем объект типа StepMotor, задаем выводы для фаз
16 
17void setup() {
18Timer1.initialize(250); // инициализация таймера 1, период 250 мкс
19Timer1.attachInterrupt(timerInterrupt, 250); // обработчик прерываний
20myMotor.setMode(0, false); // шаговый режим, без фиксации при остановке
21myMotor.setDivider(15); // делитель частоты 15
22}
23 
24void loop() {
25// проверка остановки двигателя
26if( myMotor.readSteps() == 0) {
27// двигатель остановился
28 
29// вычисление заданного положения
30setStep = averageU * (numStepsMotor - 1) / 1023 / MEASURE_PERIOD;
31 
32// определение сколько шагов надо сделать
33int stepsToDo; // сколько шагов надо сделать
34 
35stepsToDo = currentStep - setStep; // ошибка рассогласования
36 
37if( abs(stepsToDo) >= (numStepsMotor / 1) ) {
38 
39if((stepsToDo) > 0) stepsToDo -= numStepsMotor;
40else stepsToDo += numStepsMotor;
41}
42 
43myMotor.step(stepsToDo); // запуск двигателя
44currentStep = setStep; // перегрузка текущего положения
45}
46}
47 
48//------------------------------------— обработчик прерывания 250 мкс
49void timerInterrupt() {
50myMotor.control(); // управвление двигателем
51 
52sumU += analogRead(A0); // суммирование кодов АЦП
53timeCount++; // +1 счетчик выборок усреднения
54 
55// проверка числа выборок усреднения
56if ( timeCount >= MEASURE_PERIOD ) {
57timeCount= 0;
58averageU= sumU; // перегрузка среднего значения
59sumU= 0;
60}
61}

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

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

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

NEVEC пишет:

Вот скетч:

1// программа следящего электропривода без обратной связи
2 
3#include <StepMotor.h>

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