Плавное увеличение скорости вращения шагового двигателя
- Войдите на сайт для отправки комментариев
Ср, 10/10/2018 - 12:56
Добрый день!
Написал программу для управления шаговым двигателем с помощью тактовых кнопок - запуск/остановка двигателя, изменение шага приращения скорости, уменьшение/увеличение скорости. Но при нажатии на кнопки во время вращения двигателя, шаговик на доли секунды прекращает вращаться и затем раскручивается с новой скоростью. Возможно ли изменить программу так, чтобы не было данных остановок?
Код программы:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>
LiquidCrystal_I2C lcd(0x3F, 16, 2); //SCL - A5, SDA - A4
AccelStepper stepper(1, 0, 1); // 0 - STEP, 1 - DIR
int dSteps[6] = {1, 2, 5, 10, 25, 50}; // Набор шагов для регулировки скорости
int dStep = dSteps[0]; // Начальный шаг регулировки
int SPEED = 200; // Начальная скорость вращения двигателя шаг/с = 60 об/мин
int button = 1021; // Ниодна из кнопок не нажата
int state = 0; // Состояние дисплея: 0 - Standby, 1 - Working
int count = 0; // Счетчик для изменения шага
void setup()
{
// initialize the LCD
lcd.begin();
lcd.clear();
// Turn on the blacklight and print a message.
lcd.backlight();
Standby(); // При запуске на экране отображается набор Standby
stepper.setMaxSpeed(SPEED);
stepper.setAcceleration(50);
}
void loop() {
ButtonRelease();
if (state == 1) {
stepper.setSpeed(SPEED);
stepper.run();
}
}
void Standby() { // Режим ожидания - шаговик стоит
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("STANDBY");
lcd.setCursor(0, 1);
lcd.print("Press START");
SPEED = 200;
dStep = dSteps[0];
count = 0;
}
void Working() { // Режим работы - шаговик вращается
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Speed RPM: ");
lcd.print(SPEED * 0.3);
lcd.setCursor(0, 1);
lcd.print("Step RPM: ");
lcd.print(dStep * 0.3);
}
void ButtonRelease() // Отслеживание нажатия кнопок
{
int newButton = GetKeyValue();
if (button != newButton) {
button = newButton;
switch (button) {
case 1:
if (state == 0)
{
Working();
state = 1;
}
else if (state == 1)
{
Standby();
state = 0;
}
break;
case 2:
count++;
if (count > 5) count = 0;
dStep = dSteps[count];
Working();
break;
case 3:
SPEED = SPEED + dStep;
Working();
break;
case 4:
SPEED = SPEED - dStep;
if (SPEED < 100) SPEED = 100;
Working();
break;
}
}
}
int GetKeyValue() // Функция, устраняющая дребезг
{
static int count1;
static int oldKeyValue; // Переменная для хранения предыдущего значения состояния кнопок
static int innerKeyValue;
// Здесь уже не можем использовать значение АЦП, так как оно постоянно меняется в силу погрешности
int actualKeyValue = GetButtonNumberByValue(analogRead(0)); // Преобразовываем его в номер кнопки, тем самым убирая погрешность
if (innerKeyValue != actualKeyValue) { // Пришло значение отличное от предыдущего
count1 = 0; // Все обнуляем и начинаем считать заново
innerKeyValue = actualKeyValue; // Запоминаем новое значение
}
else {
count1 += 1; // Увеличиваем счетчик
}
if ((count1 >= 10) && (actualKeyValue != oldKeyValue)) {
oldKeyValue = actualKeyValue; // Запоминаем новое значение
}
return oldKeyValue;
}
// 1 - 746; 2 - 514; 3 - 343; 4 - 146; 5 - 1; покой - 1021
int GetButtonNumberByValue(int value) // Новая функция по преобразованию кода нажатой кнопки в её номер
{
//int values[6] = {1023, 724, 482, 133, 309, 0};
int values[6] = {1021, 746, 514, 146, 343, 0};
int error = 100; // Величина отклонения от значений - погрешность
for (int i = 0; i <= 5; i++) {
// Если значение в заданном диапазоне values[i]+/-error - считаем, что кнопка определена
if (value <= values[i] + error && value >= values[i] - error) return i;
}
return -1; // Значение не принадлежит заданному диапазону
}
А если отключить функцию Working() эффект сохраняется?
А если отключить функцию Working() эффект сохраняется?
Закомментировал Working в функции ButtonRelease() и остановок больше нет.
Но как тогда сделать, чтобы на lcd обновлялась информация о текущей скорости?
Как минимум - не делать clear, не перепечатывать "Speed RPM" и пр, выводить только цифры в смещенную позицию
Как минимум - не делать clear, не перепечатывать "Speed RPM" и пр, выводить только цифры в смещенную позицию
то есть с помощью setCursor вставать на место печатаемых цифр?
Типа того. И после цифр еще пару пробелов печатать.
Типа того. И после цифр еще пару пробелов печатать.
сделал так:
void ButtonRelease() // Отслеживание нажатия кнопок { int newButton = GetKeyValue(); if (button != newButton) { button = newButton; switch (button) { case 1: if (state == 0) { Working(); state = 1; } else if (state == 1) { Standby(); state = 0; } break; case 2: count++; if (count > 5) count = 0; dStep = dSteps[count]; lcd.setCursor(10, 1); lcd.print(dStep * 0.3); lcd.print(" "); break; case 3: SPEED = SPEED + dStep; lcd.setCursor(11, 0); lcd.print(SPEED * 0.3); lcd.print(" "); break; case 4: SPEED = SPEED - dStep; if (SPEED < 100) SPEED = 100; lcd.setCursor(11, 0); lcd.print(SPEED * 0.3); lcd.print(" "); break; } } }Но все равно дергается при нажатии. Если в кейсах убрать строки с lcd, то тогда не дергается, но и на экран не вывести текущие параметры
Ну, что ж поделать... Есть случаи, когда простые способы не помогают и надо учиться ходить на руках.
Суть-то проблемы вы поняли? Без этого двигаться дальше вы не сумеете.
...и не использовали бы вы выходы 0/1 для степпера. Вот сейчас понадобится выводить отладочные сообщения в Serial - что вы получите?
Ну, что ж поделать... Есть случаи, когда простые способы не помогают и надо учиться ходить на руках.
Суть-то проблемы вы поняли? Без этого двигаться дальше вы не сумеете.
...и не использовали бы вы выходы 0/1 для степпера. Вот сейчас понадобится выводить отладочные сообщения в Serial - что вы получите?
Поможет ли библиотека ArduinoThread?
Поможет в чем? Вы суть проблемы поняли? Если нет, то можете прикладывать все библиотеки подряд. И подорожник еще.
Поможет в чем? Вы суть проблемы поняли? Если нет, то можете прикладывать все библиотеки подряд. И подорожник еще.
Ну если я правильно понял, то проблема в том, что когда я что-то пишу на дисплей, отрубается шаговик. Следовательно нужно как-то сделать, чтобы работа шаговика и работа дисплея были независимы друг от друга.
Наполовину правильно поняли. Поставьте вместо вывода на дисплей простой delay(500); и понаблюдайте.
Наполовину правильно поняли. Поставьте вместо вывода на дисплей простой delay(500); и понаблюдайте.
По сути происходит то же самое, что и при выводе на дисплей - задержка.
Ну вот, вывод отсюда какой? Мешает не печать на LCD, а задержка на ее время. Если междушаговый интервал у вас меньше, чем время печати, то хоть тресни - ничего не поделаете. Т.е. можете, но придется жить без библиотеки, писать общение по I2C вручную... Или как-то делить команды - установка курсора, потом шаг, потом печать куска, потом шаг и т.д.
Надо мерять, сколько занимает вывод в дисплей и пр. и др. Для этого вам Serial на выходах 0/1 и пригодится - будете миллис писать туда перед началом печати на LCD и после, потом сравнивать с междушаговым интервалом.
Понял, буду искать решение проблемы. Спасибо
Я бы еще посоветовал умножение убрать, а прибавлять/убавлять по 0.3. Несильно, но задержку уменьшит. А там... курочка по зернышку, так скыть.
От умножения никак не избавиться, по-моему. Ведь на шаговик подается скорость в шаг/с, а на экран выводится в об/мин. Если только не заводить еще 2 переменные, в которых будет производиться перевод.
В силу скудных знаний в области программирования, пришла на ум идея использовать 2 ардуинки, одна по нажатии кнопок будет отрабатывать действия с шаговиком, вторая выводить на экран, благо все кнопки через один пин подключены. Совсем глупо придумал или есть в этом что-то?
есть ещё вариант - три дуни.
одна мотор крутит, вторая экран рисует, третья за кнопками следит.
Сарказм?
Понимаю, что использовать 2 дуни скорей всего глупо, но другого варианта для себя я не вижу пока что.
вам уже написали что делать - на дисплее во время работы двигателя прорисовывать толко изменяемое значение.
кстати, у вас нет ограничения скорости по верхнему пределу.
для этой библиотеки больше 1000 может двигатель встать)))
вам уже написали что делать - на дисплее во время работы двигателя прорисовывать толко изменяемое значение.
так прорисовка занимает 2 строки кода и все равно вызывает кратковременную остановку в работе двигателя
а максимальное значение в setup стоит же и выше него двигатель крутить не будет, только на дисплее значение меняться будет (надо поправить это)
Такой костыль не поможет?
... lcd.setCursor(10, 1); stepper.run() lcd.print(dStep * 0.3); stepper.run() lcd.print(" "); stepper.run() ...упёртый вы однако.
у вас перерисовывается весь дисплей. а нужно только
lcd.print(SPEED * 0.3); илиlcd.print(dStep * 0.3);упёртый вы однако.
у вас перерисовывается весь дисплей. а нужно только
lcd.print(SPEED * 0.3); илиlcd.print(dStep * 0.3);я в 6-м комментарии приводил переделанный фрагмент кода. там перерисовываются только цифры
ладно, если не можете победить - скачайте библиотеку stepperq и будете выводить что захочется.
её автор на форуме ссылку давал на неё, поищите поиском.
Сколько нужно ардуин что бы закрутить .... шаговик?
А если отключить функцию Working() эффект сохраняется?
Закомментировал Working в функции ButtonRelease() и остановок больше нет.
Но как тогда сделать, чтобы на lcd обновлялась информация о текущей скорости?
Собственно, я никогда не использовал AccelStepper, поэтому не знаю, как она работает. Поэтому задал вопрос, ответ на который прояснил для меня ситуацию.
В общем, как выяснилось из Вашего ответа, шаговик может сделать шаг только тогда, когда Вы вызываете stepper.run(). Соответственно, интервал между двумя вызовами не должен превышать минимальной длительности шага (оцените - какая Вам нужна).
Как-то мне нужно было выводить на аналогичный дисплей в условиях, когда нельзя было приостанавливать программу (для вашего случая - это как раз минимальная длительность шага) более чем на 0.3 мс. В то же время я выяснил, что один (!) символ выводится на экран целых 1.5 мс. Создал тему: http://arduino.ru/forum/apparatnye-voprosy/medlennaya-rabota-liquidcrystali2c
и совместными усилиями удалось увеличить скорость вывода символа практически в 10 раз.
С одним символом - все хорошо, но для вывода строки целиком явно недостаточно времени. Написал класс, который принимает строку и через метод аналогичный stepper.run() постепенно выводит ее по одному символу на экран.
Так можно сделать и в Вашем случае: сделал шаг, вывел символ, сделал шаг, вывел символ... Ну, разумеется, сначала добившись более быстрой работы экрана, как предложено в упомянутой выше теме.
Есть и другой вариант - сделать, чтобы шаги совершались не по вызову .run, а по таймеру. Возможно, это уже реализовано в какой-либо библиотеке для шаговых двигателей.
Чой-то я наверное не понял: что мешает поюзать тот же TimerOne, и ручками шагать по тикам таймера? Скорость вращения настраивать интервалами между тиками таймера, как вариант. И пофиг, чего там куда выводится - по прерыванию работаем.
Как я писал: таймер - один из вариантов.
А уж сам таймер будет выдавать сигнал на предопределенной ноге или делегирует эту возможность ЦПУ посредством прерывания - дело десятое.
точно. можно мои таймеры сюда прикрутить. наерна. с шаговиками я плотно не работал
точно. можно мои таймеры сюда прикрутить. наерна. с шаговиками я плотно не работал
Деда, а чего там работать, если по STEP/DIR? Тикнул таймер, дёрнули STEP в высокий, подождали 10 микросекунд, дёрнули step в низкий. Перед началом движения DIR в высокий или низкий, в зависимости от направления вращения. После окончания движения - EN в высокий или низкий, в зависимости от того - нужно ли удержание или нет. Короче, не квадратный многочлен, даже близко.
10 много даже. Пять - уже с запасом. A4988 требует удержания состояния в обоих положениях на интервал в 1uS, DRV8824 - 1.9uS, TB6600 - 2,2uS.
10 много даже. Пять - уже с запасом. A4988 требует удержания состояния в обоих положениях на интервал в 1uS, DRV8824 - 1.9uS, TB6600 - 2,2uS.
Зато надёжа :) На хоббийных применениях скорости шагания не ахти требуются, так что там те 10 микросекунд - как слону известно что, как правило. Но согласен - смело можно уменьшать.
Скорость я планировал менять в большом диапазоне (от 100 до 1000 об/мин), то есть получается для каждой установленной скорости нужно производить пересчет интервала? И в целом работать без библиотек для шаговика, меняя HIGH и LOW на ногах драйвера? И метод работы шаговика закинуть в прерывания? Тогда в loop() у меня будет крутиться только работа с дисплеем. Правильно понимаю идею?
Типа того. Только с разгоном-торможением не выйдет так просто, как с библиотекой. Ну, это, конечно если нужна трапеция в графике движения. Если нет - все проще. ...Wire, по-моему в прерывания не лезет, не блокирует?
Wire использует аналоговые выходы, а, как я понял, прерывания только на цифровых работают.
И возник глупый вопрос. Если шаговик работает по прерываниям, смогу ли я его включать выключать по нажатию кнопки, аналогично тому как это у меня было реализовано до этого?
Wire использует аналоговые выходы, а, как я понял, прерывания только на цифровых работают.
Не могу согласиться, однако к теме данный диспут отношения иметь не будет.
И возник глупый вопрос. Если шаговик работает по прерываниям, смогу ли я его включать выключать по нажатию кнопки, аналогично тому как это у меня было реализовано до этого?
Почему нет? В ISR проверяйте - переменная true? Да - шагаем, нет - выпрыгиваем сразу.
По крайней мере i2c плата подключается к ардуине через аналоговые пины
По крайней мере i2c плата подключается к ардуине через аналоговые пины
Просто к этим входам МК может подключаться встроенное АЦП. А может и не подключаться. К другим АЦП не подключается в принципе. К третьим.. ну, которые A6/A7 - только АЦП подключается.
А "аналоговыми"/"цифровыми" их назвали создатели платформы Ардуино. Чисто для начинающих.
И такой вопрос: что мне нужно почитать, чтобы разобраться с реализацией вращения шаговика по такому принципу?
То есть можно как-то переобозначить пины А4/А5 для i2c на пины, например, D4/D5 и на экран будет все также выводиться?
Ключевые слова даны: TimerOne, STEP/DIR/ENABLE управление. Ну, и математику вспомнить для пересчета RPM в Delay.
То есть можно как-то переобозначить пины А4/А5 для i2c на пины, например, D4/D5 и на экран будет все также выводиться?
для 328 - нельзя переназначить
Понял. Буду разбираться. Спасибо
Значит вариант только один у меня - использовать прерывания на шаговике
То есть можно как-то переобозначить пины А4/А5 для i2c на пины, например, D4/D5 и на экран будет все также выводиться?
Ничего не надо переобозначать. A4 и A5 - это макросы для выходов #18 и #19. На ардуину посмотрите - там D* до 13-го идут, а потом A*, A0 - на самом деле #14, A1 - #15 и т.д.
Просто из #42 сообщения я понял, что можно i2c навесить на другие ноги - и использовать прерывания на нем. Видимо неправильно понял...