Плавное увеличение скорости вращения шагового двигателя
- Войдите на сайт для отправки комментариев
Ср, 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 вставать на место печатаемых цифр?
Типа того. И после цифр еще пару пробелов печатать.
Типа того. И после цифр еще пару пробелов печатать.
сделал так:
Но все равно дергается при нажатии. Если в кейсах убрать строки с 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.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 навесить на другие ноги - и использовать прерывания на нем. Видимо неправильно понял...