Шаговый двигатель (оптимизация)
- Войдите на сайт для отправки комментариев
Пнд, 25/09/2017 - 15:26
Добрый день! Сделал на базе ардуино и шагового двигателя (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;
}
}

Прошу помочь
(Шаговый двигатель 28BYJ-84, драйвер на основе ULN2003)
1) Можно ли этот код оптимизировать?
Оптимизировать можно всё. Другой вопрос, что Вы не указали цель оптимизации.
А код подробно не глядел, но на первый взгляд выглядит прилично. Стесняюсь спросить ... сами писали?
Брал отсюда http://www.techclub.su/article_arduino06. Цель оптимизации минимальное энергопотребление! Что бы включил в розетку и забыл.
В данный момент при остановке шаговик фиксирует себя и наченает греться, не могу понять как отключить эту функцию.
Так же могу ли я скажем так парралельно добавить шаговый двигатель после драйвера или через ардуионо?
Движок, как правило, отключается подаванием одинаковых уровней на драйвер мотора, читайте доку к своему драйверу. Например, подав HIGH на оба управляющих входа H-моста - можно отключить мотор, при этом ротор будет вращаться руками свободно. Короче - всё зависит от драйвера, смотрите доку по нему.
Нет, это другой драйвер - для 4 ф униполярного шаговика. Там 4 входа и 4 выхода. Дал High- выход на землю (open collector), дал Low- выход болтается. Это просто четверной дарлингтон 2003.
Только с двумя драйверами. Для отключения шд во время паузы подайте LOW на все пины управления драйвером ШД
/*Программа для шагового двигателя 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 светодиод на драйвере(
после
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); }Нет, нет и нет - не делайте так, как только что посоветовали. Да, надо отключить фазы, чтобы зря не гонять, но делать это в каждом шаге нельзя, от слова совсем. Заведите таймер и если фаза не меняется в течении длительнго времени, только тогда отключайте, ну или если нет управляющего воздействия (нет кнопок) - на Ваш выбор, но не в каждом такте.
А по поводу параллельных моторов - да пожалуйста, если они идентичны и скорость шага приемлема для всех сразу.
ButtonState и так уже очищена от дребезга, разве нет?
и задержка уже есть есть
delay(TurnPhasesDelay);
Можно (как вариант) в разрез на драйвер сборку реле 4х канальное. При включении реле замыкает провод. Отработал шаговик - вырубить реле.
Вот когдато писал для себя, может и Вам подойдет
//********** (РЕЖИМ 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; } }Увы для меня не подходят!
Нашел в просторах ютуба интересный ролик 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%) при отключении и вновь подачи питания шаговый двигатель прокручивается на определенный шаг сбивается калибровка (т.е может сломать жалюжи). Можно ли доработать данную схему. Ну конечно не бесплатно!
Вот скетч:
NEVEC здравствуй. А библиотеку можно от вас получить. Я так понял ее отдельно писали.