Шаговый двигатель (оптимизация)
- Войдите на сайт для отправки комментариев
Пнд, 25/09/2017 - 15:26
Добрый день! Сделал на базе ардуино и шагового двигателя (28BYJ-48) управления вертикальным жалюзи.
Есть скетч, но есть 3 момента:
1) Можно ли этот код оптимизировать?
2) Не нашел как отключить фиксацию ротора шагового двигателя, движок грееться и расходует лишнюю энергию.
3) Подключить дополнительно дублирующие щаговые двигатели (сколько можно их подключить?) В идеале хочу добиться управления 6ти вертикальных жалюжи, 3(независимое упр)+3 (независимое упр).
001 | /*Программа для шагового двигателя 28BYJ-48 (5V). В схеме есть кнопка и потенциометр. В зависимости от положения кнопки (пол. 1, пол. 2, выключено) мотор вращается либо в одну сторону, либо в другую, либо стоит на месте, а потенциометр влияет на скорость вращения.*/ |
002 | /*У данного мотора 4 провода (оранж., жёлт., розов., син.), которые мы подключаем к контактам ардуино. Номера контактов указываем в массиве MotorPins, в порядке, соответствующем перечислению цветов, в нашем случае с D12 по D9*/ |
003 | int MotorPins[4] = {8, 9, 10, 11}; |
004 |
005 | /*Контакты от двух положений кнопки - цифровые*/ |
006 | const int ButtonOn1 = 5; |
007 | const int ButtonOn2 = 4; |
008 |
009 | /*Контакт регистрирующий значение потенциометра - аналоговый*/ |
010 | const int PotenciomData = 0; |
011 |
012 | /*Целочисленная константа, показывающая количество фаз подачи сигналов для одного шага мотора. Для полушагового режима - 8 |
013 | Для шагового - 4*/ |
014 | const int OneTurnPhasesCount = 8; |
015 |
016 | /*Целочисленная переменная, показывающая задержку в миллисекундах между фазами подачи сигналов мотору. Для полушагового режима - 2, |
017 | для шагового - 3*/ |
018 | int TurnPhasesDelay = 2; |
019 |
020 | /*Целочисленная переменная, показывающая номер текущей фазы*/ |
021 | int CurrentPhase = 0; |
022 |
023 | //состояние кнопки включено-выключено |
024 | int ButtonState = 0; |
025 |
026 | /*Целочисленная переменная, показывающая направление вращения мотора: 1 - по часовой стрелке, 0 - против*/ |
027 | int TurnDirection = 1; |
028 |
029 | /*целочисленная константа, показывающая временную задержку между считыванием состояния кнопки и потенциометра*/ |
030 | const int CheckButtonDelay = 15; |
031 |
032 | /*Целочисленная переменная показывающая, сколько прошло времени и не пора ли считывать состояние кнопки*/ |
033 | int CurrentButtonDelay = 0; |
034 |
035 | //Для полушагового режима |
036 |
037 | /*Массив, в котором указано какие сигналы подавать на контакты мотора в той или иной фазе. [фаза][контакт]. Контакты даются в порядке, перечисленном в массиве MotorPins - оранж., жёлт., розов., син. 0 - нет сигнала, 1 - есть сигнал*/ |
038 | bool 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 | /*Функция, в которой происходит инициализация всех переменных программы*/ |
049 | void 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 | /*Функция-цикл в которой задаётся поведение программы*/ |
061 | void 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*/ |
091 | void CheckLastPhase() |
092 | { |
093 | if (CurrentPhase >= OneTurnPhasesCount) |
094 | { |
095 | CurrentPhase = 0; |
096 | } |
097 | if (CurrentPhase < 0) |
098 | { |
099 | CurrentPhase = (OneTurnPhasesCount-1); |
100 | } |
101 | } |
102 |
103 | /*функция, в которой проверяется текущее состояние кнопки*/ |
104 | void 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 | } |
Прошу помочь
(Шаговый двигатель 28BYJ-84, драйвер на основе ULN2003)
1) Можно ли этот код оптимизировать?
Оптимизировать можно всё. Другой вопрос, что Вы не указали цель оптимизации.
А код подробно не глядел, но на первый взгляд выглядит прилично. Стесняюсь спросить ... сами писали?
Брал отсюда http://www.techclub.su/article_arduino06. Цель оптимизации минимальное энергопотребление! Что бы включил в розетку и забыл.
В данный момент при остановке шаговик фиксирует себя и наченает греться, не могу понять как отключить эту функцию.
Так же могу ли я скажем так парралельно добавить шаговый двигатель после драйвера или через ардуионо?
Движок, как правило, отключается подаванием одинаковых уровней на драйвер мотора, читайте доку к своему драйверу. Например, подав HIGH на оба управляющих входа H-моста - можно отключить мотор, при этом ротор будет вращаться руками свободно. Короче - всё зависит от драйвера, смотрите доку по нему.
Нет, это другой драйвер - для 4 ф униполярного шаговика. Там 4 входа и 4 выхода. Дал High- выход на землю (open collector), дал Low- выход болтается. Это просто четверной дарлингтон 2003.
Только с двумя драйверами. Для отключения шд во время паузы подайте LOW на все пины управления драйвером ШД
001
/*Программа для шагового двигателя 28BYJ-48 (5V). В схеме есть кнопка и потенциометр. В зависимости от положения кнопки (пол. 1, пол. 2, выключено) мотор вращается либо в одну сторону, либо в другую, либо стоит на месте, а потенциометр влияет на скорость вращения.*/
002
/*У данного мотора 4 провода (оранж., жёлт., розов., син.), которые мы подключаем к контактам ардуино. Номера контактов указываем в массиве MotorPins, в порядке, соответствующем перечислению цветов, в нашем случае с D12 по D9*/
003
int
MotorPins[4] = {8, 9, 10, 11};
004
005
/*Контакты от двух положений кнопки - цифровые*/
006
const
int
ButtonOn1 = 5;
007
const
int
ButtonOn2 = 4;
008
009
/*Контакт регистрирующий значение потенциометра - аналоговый*/
010
const
int
PotenciomData = 0;
011
012
/*Целочисленная константа, показывающая количество фаз подачи сигналов для одного шага мотора. Для полушагового режима - 8
013
Для шагового - 4*/
014
const
int
OneTurnPhasesCount = 8;
015
016
/*Целочисленная переменная, показывающая задержку в миллисекундах между фазами подачи сигналов мотору. Для полушагового режима - 2,
017
для шагового - 3*/
018
int
TurnPhasesDelay = 2;
019
020
/*Целочисленная переменная, показывающая номер текущей фазы*/
021
int
CurrentPhase = 0;
022
023
//состояние кнопки включено-выключено
024
int
ButtonState = 0;
025
026
/*Целочисленная переменная, показывающая направление вращения мотора: 1 - по часовой стрелке, 0 - против*/
027
int
TurnDirection = 1;
028
029
/*целочисленная константа, показывающая временную задержку между считыванием состояния кнопки и потенциометра*/
030
const
int
CheckButtonDelay = 15;
031
032
/*Целочисленная переменная показывающая, сколько прошло времени и не пора ли считывать состояние кнопки*/
033
int
CurrentButtonDelay = 0;
034
035
//Для полушагового режима
036
037
/*Массив, в котором указано какие сигналы подавать на контакты мотора в той или иной фазе. [фаза][контакт]. Контакты даются в порядке, перечисленном в массиве MotorPins - оранж., жёлт., розов., син. 0 - нет сигнала, 1 - есть сигнал*/
038
bool
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
/*Функция, в которой происходит инициализация всех переменных программы*/
049
void
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
/*Функция-цикл в которой задаётся поведение программы*/
061
void
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*/
093
void
CheckLastPhase()
094
{
095
if
(CurrentPhase >= OneTurnPhasesCount)
096
{
097
CurrentPhase = 0;
098
}
099
if
(CurrentPhase < 0)
100
{
101
CurrentPhase = (OneTurnPhasesCount-1);
102
}
103
}
104
105
/*функция, в которой проверяется текущее состояние кнопки*/
106
void
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 светодиод на драйвере(
после
01
069
02
if
(ButtonState != 0)
03
070
04
{
05
071
06
//проверяем индекс текущей фазы
07
072
08
CheckLastPhase();
09
073
10
11
074
12
/*подаём напряжения на контакты мотора соответственно фазе, заданной в массиве MotorTurnPhases*/
13
075
14
for
(
int
i = 0; i < 4; i++)
15
076
16
{
17
077
18
digitalWrite(MotorPins[LOW], ( (MotorTurnPhases[CurrentPhase][i] == 1) ? HIGH : LOW) );
19
078
20
}
21
079
22
23
080
24
//переходим к другой фазе
25
081
26
CurrentPhase += TurnDirection;
27
082
28
29
083
30
// Пауза между фазами
31
084
32
delay(TurnPhasesDelay);
33
085
34
35
086
36
37
087
38
}
напишите
1
else
2
{
3
digitalOut(9,LOW);
4
digitalOut(10,LOW);
5
digitalOut(11,LOW);
6
digitalOut(12,LOW);
7
8
}
Нет, нет и нет - не делайте так, как только что посоветовали. Да, надо отключить фазы, чтобы зря не гонять, но делать это в каждом шаге нельзя, от слова совсем. Заведите таймер и если фаза не меняется в течении длительнго времени, только тогда отключайте, ну или если нет управляющего воздействия (нет кнопок) - на Ваш выбор, но не в каждом такте.
А по поводу параллельных моторов - да пожалуйста, если они идентичны и скорость шага приемлема для всех сразу.
ButtonState и так уже очищена от дребезга, разве нет?
и задержка уже есть есть
delay(TurnPhasesDelay);
Можно (как вариант) в разрез на драйвер сборку реле 4х канальное. При включении реле замыкает провод. Отработал шаговик - вырубить реле.
Вот когдато писал для себя, может и Вам подойдет
001
//********** (РЕЖИМ 1-ШАГ) Крутим с помощью сдвига --рабочая - крутит туда сюда без потери шагов - ускоряет - отключает катушки после бездействия
002
//PORTB - 8, 9, 10, 11 - Шаговик. && 12, 13 - Вход сигнал от 2х кнопок (влево - вправо)
003
004
#define uskor 5 //Задаем ускорение
005
byte
n1 = 1;
// - Сдвигаем 1 бит;
006
long
int
perem_X = 0;
//Задаём количество шагов и направление
007
byte
f_tim = 0;
008
//byte varB_X = 0;
009
//byte varM_X = 0;
010
011
byte
sped_Xt = uskor;
//Начальная скорость
012
byte
sped_X = 0;
//Ускорение
013
bool
f_run1 = 0;
//Сброс после остановки 2 переменные.
014
bool
f_run2 = 0;
015
//bool f_run3 = 0;
016
unsigned
long
no_run;
//Переменная для прошедшего периода
017
018
void
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
037
ISR (TIMER1_COMPA_vect)
// Функция прерывания таймера
038
{
039
// Serial.println(f_tim);
040
f_tim = 1;
041
}
042
043
//Функция вращения 1 dvigatel
044
void
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
059
void
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
}
Увы для меня не подходят!
Нашел в просторах ютуба интересный ролик 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
09
int
timeCount;
// счетчик времени
10
long
sumU;
// переменные для суммирования кодов АЦП
11
long
averageU;
// сумма кодов АЦП (среднее значение * 80)
12
int
currentStep;
// текущее положение двигателя
13
int
setStep;
// заданное положение двигателя
14
15
StepMotor myMotor(8, 9, 10, 11);
// создаем объект типа StepMotor, задаем выводы для фаз
16
17
void
setup
() {
18
Timer1.initialize(250);
// инициализация таймера 1, период 250 мкс
19
Timer1.attachInterrupt(timerInterrupt, 250);
// обработчик прерываний
20
myMotor.setMode(0,
false
);
// шаговый режим, без фиксации при остановке
21
myMotor.setDivider(15);
// делитель частоты 15
22
}
23
24
void
loop
() {
25
// проверка остановки двигателя
26
if
( myMotor.readSteps() == 0) {
27
// двигатель остановился
28
29
// вычисление заданного положения
30
setStep = averageU * (numStepsMotor - 1) / 1023 / MEASURE_PERIOD;
31
32
// определение сколько шагов надо сделать
33
int
stepsToDo;
// сколько шагов надо сделать
34
35
stepsToDo = currentStep - setStep;
// ошибка рассогласования
36
37
if
( abs(stepsToDo) >= (numStepsMotor / 1) ) {
38
39
if
((stepsToDo) > 0) stepsToDo -= numStepsMotor;
40
else
stepsToDo += numStepsMotor;
41
}
42
43
myMotor.step(stepsToDo);
// запуск двигателя
44
currentStep = setStep;
// перегрузка текущего положения
45
}
46
}
47
48
//------------------------------------— обработчик прерывания 250 мкс
49
void
timerInterrupt() {
50
myMotor.control();
// управвление двигателем
51
52
sumU += analogRead(A0);
// суммирование кодов АЦП
53
timeCount++;
// +1 счетчик выборок усреднения
54
55
// проверка числа выборок усреднения
56
if
( timeCount >= MEASURE_PERIOD ) {
57
timeCount= 0;
58
averageU= sumU;
// перегрузка среднего значения
59
sumU= 0;
60
}
61
}
полностью рабочая (проверял) с возможностью переключеня в авто режим (фототранзистор).
Но есть недостаток если потенциометр например в положении 50% (0-100%) при отключении и вновь подачи питания шаговый двигатель прокручивается на определенный шаг сбивается калибровка (т.е может сломать жалюжи). Можно ли доработать данную схему. Ну конечно не бесплатно!
Вот скетч:
1
// программа следящего электропривода без обратной связи
2
3
#include <StepMotor.h>
NEVEC здравствуй. А библиотеку можно от вас получить. Я так понял ее отдельно писали.