Шаговый двигатель + LCD
- Войдите на сайт для отправки комментариев
Доброе время суток!
В программировании новичок, только начал осваивать язык arduino (или правильно С/C+). Нужна помощь в корректировке кода для управления ШД с выводом информации на LCD. Имеется биполярный шаговый двигатель, драйвер шагового двигателя на микросхеме А4988, LCD дисплей WH1602B и Arduino UNO.
Принцип работы такой - имеется некоторый диапозон скоростей, из которого необходимо выбрать нужный и затем запустить в работу ШД.
Кнопка В - "вверх" (выбор большего значения из диапозона скоростей);
Кнопка С - "вниз" (выбор меньшего значения из диапозона скоростей);
Кнопка А - "принять" (назначаем выбранную скорость для работы двигателя и включаем двигатель);
Кнопка D - "стоп" (останавливаем двигатель).
Искал на форуме информацию по этой теме, взял для основы скетч пользователя maksim, немного изменил и получилось следующее:
#include <AccelStepper.h>
#include <LiquidCrystal.h>
#define button_A A0
#define button_B A1
#define button_C A2
#define button_D A3
LiquidCrystal lcd(12, 11, 10, 9, 8, 7);
AccelStepper stepper(1, 3, 2);
int speeds[7] = {350, 300, 250, 200, 150, 100, 50};
int Speed = 0;
void setup()
{
pinMode(14, OUTPUT);
pinMode(15, OUTPUT);
pinMode(16, OUTPUT);
pinMode(17, OUTPUT);
digitalRead(button_A);
digitalRead(button_B);
digitalRead(button_C);
digitalRead(button_D);
stepper.setAcceleration(1000.0);
stepper.setMaxSpeed(1000);
lcd.begin(16, 2);
lcd.print("Speed: ");
}
void loop()
{
if(digitalRead(button_B) == HIGH)
{
Speed++;
if(Speed == 7) Speed = 0;
delay(200);
}
if(digitalRead(button_C) == HIGH)
{
Speed--;
if(Speed == -1) Speed = 6;
delay(200);
}
static int SpeedPrev;
if(Speed != SpeedPrev)
{
lcd.setCursor(7, 0);
lcd.print(speeds[Speed]);
SpeedPrev = Speed;
}
if(digitalRead(button_A) == HIGH)
{
stepper.setSpeed(speeds[Speed]);
}
if(digitalRead(button_D) == HIGH)
{
stepper.stop();
}
stepper.run();
}
При включении на экранее появляется "Speed: ", выбираю нужную скорость - далее нажимаю принять, но ШД крутится только при удержании кнопки, когда отпускаю - ШД останавливается и возвращается с максимальной скоростью в исходное положение.
В чём может быть проблема?
if(digitalRead(button_A) == HIGH) { stepper.setSpeed(speeds[Speed]); } if(digitalRead(button_D) == HIGH) { stepper.setSpeed(0); } stepper.runSpeed(); }Спасибо! Теперь код заработал.
Появилась проблема - при изменении скорости (в момент нажатия на кнопку "вверх"/"вниз") ШД останавливается, после отпускания кнопки он продолжает выполнять программу. Я читал на форуме, про подобные проблемы с использование ШД + LCD, но ответов не нашёл. Это как-то решается?
Уберите задержки, но тогда скорость будет быстро меняться.
Действительно, без задержек скорость выбора не контролируема. Решил это благодаря мануалу Работа с кнопка. В помощи новичку. Использовал переменную для сохранения изменения кнопки в виде 1 или 0. Вот что получилось:
#include <AccelStepper.h> #include <LiquidCrystal.h> #define button_A A0 #define button_B A1 #define button_C A2 #define button_D A3 int flag_A = 0; int flag_B = 0; int flag_C = 0; int flag_D = 0; LiquidCrystal lcd(12, 11, 10, 9, 8, 7); AccelStepper stepper(1, 3, 2); int speeds[9] = {900, 800, 700, 600, 500, 400, 300, 200, 100}; int Speed = 0; void setup() { pinMode(14, OUTPUT); pinMode(15, OUTPUT); pinMode(16, OUTPUT); pinMode(17, OUTPUT); digitalRead(button_A); digitalRead(button_B); digitalRead(button_C); digitalRead(button_D); stepper.setAcceleration(10.0); stepper.setMaxSpeed(1000); lcd.clear(); lcd.begin(16, 2); lcd.print("Speed: "); lcd.setCursor(11, 0); lcd.print("RPMs"); } void loop() { if(digitalRead(button_B) == HIGH && flag_B == 0) { Speed++; if(Speed == 9) Speed = 0; flag_B = 1; } if(digitalRead(button_B) == LOW && flag_B == 1) { flag_B = 0; } if(digitalRead(button_C) == HIGH && flag_C == 0) { Speed--; if(Speed == -1) Speed = 8; flag_C = 1; } if(digitalRead(button_C) == LOW && flag_C == 1) { flag_C = 0; } static int SpeedPrev; if(Speed != SpeedPrev) { lcd.setCursor(7, 0); lcd.print(speeds[Speed]); SpeedPrev = Speed; } if(digitalRead(button_A) == HIGH && flag_A == 0) { stepper.setSpeed(speeds[Speed]); flag_A = 1; } if(digitalRead(button_A) == LOW && flag_A == 1) { flag_A = 0; } if(digitalRead(button_D) == HIGH && flag_D == 0) { stepper.setSpeed(0); flag_D = 1; } if(digitalRead(button_D) == LOW && flag_D == 1) { flag_D = 0; } stepper.runSpeed(); }а зачем в setup прописаны digitalRead?
Это 4 лишних строчки кода, их нужно убрать. Спасибо что указали.
Продолжил дополнять код нужными мне функциями, но остановился на следующем. Необходимо отсчитывать пройденный путь точки, расположенной на расстоянии R от оси шагового двигателя (ШД будет работать в приборе, который работает по аналогии с протяжными механизмами проволоки в сварочных полуавтоматах). Другими словами - нужно выводить на дисплей длину протянутой "проволоки". Эта длина равна линейной скорости умноженной на время, т.е. классическое S=V*t. Все математические формулы представил в коде:
float post = 0.0471; // post - произведение Pi*2*R из формулы линейной скорости V=2*Pi*f*R, где R - радиус. Неизвестной в этой формуле является только частота вращения f, поэтому в моём случае V=0.0471*f. float k = (speeds[Speed])/(float)200; // k - коэффициент частоты, при stepper.setSpeed(200) ШД делает 1 об/с float f = (float)60 * k; // f - частота в об/мин. float v = (float)0.00471 * f; // линейная скорость в м/мин. int sek = millis() / 1000; float myn = sek / (float)60; float s = v * myn; // s - расстояние, как произведение скорости на время. lcd.setCursor(11, 1); lcd.print("DISTANCE: "); lcd.print(s); // выводим расстояние на дисплей.Принцип работы - выбираем скорость кнопками B и C, далее запускаем ШД кнопкой А - пошёл отсчёт, выводим его на дисплей. Останавливаем работу кнопкой D - остановка отсчёта. При зажатии кнопки D более чем на 3 секунды - сброс счётчика.
Грамотно вставить это в код не выходит.
Исходник:
#include <AccelStepper.h> #include <LiquidCrystal.h> #define button_A A0 #define button_B A1 #define button_C A2 #define button_D A3 int flag_A = 0; int flag_B = 0; int flag_C = 0; int flag_D = 0; LiquidCrystal lcd(12, 11, 10, 9, 8, 7); AccelStepper stepper(1, 3, 2); int speeds[10] = {900, 800, 700, 600, 500, 400, 300, 200, 100, 10}; float realspeeds[10] = {12.7, 11.3, 9.90, 8.48, 7.06, 5.65, 4.24, 2.83, 1.41, 0.14}; int Speed = 0; void setup() { pinMode(14, OUTPUT); pinMode(15, OUTPUT); pinMode(16, OUTPUT); pinMode(17, OUTPUT); stepper.setAcceleration(10.0); stepper.setMaxSpeed(1000); lcd.clear(); lcd.begin(16, 2); lcd.print("Speed: "); lcd.setCursor(11, 0); lcd.print("m/min"); lcd.setCursor(0, 1); lcd.print("DISTANCE: "); } void loop() { if(digitalRead(button_B) == HIGH && flag_B == 0) { Speed++; if(Speed == 10) Speed = 0; flag_B = 1; } if(digitalRead(button_B) == LOW && flag_B == 1) { flag_B = 0; } if(digitalRead(button_C) == HIGH && flag_C == 0) { Speed--; if(Speed == -1) Speed = 9; flag_C = 1; } if(digitalRead(button_C) == LOW && flag_C == 1) { flag_C = 0; } static int SpeedPrev; if(Speed != SpeedPrev) { lcd.setCursor(7, 0); lcd.print(realspeeds[Speed]); lcd.setCursor(11, 0); lcd.print("m/min"); SpeedPrev = Speed; } if(digitalRead(button_A) == HIGH && flag_A == 0) { stepper.setSpeed(speeds[Speed]); flag_A = 1; } if(digitalRead(button_A) == LOW && flag_A == 1) { flag_A = 0; } if(digitalRead(button_D) == HIGH && flag_D == 0) { stepper.setSpeed(0); flag_D = 1; } if(digitalRead(button_D) == LOW && flag_D == 1) { flag_D = 0; } stepper.runSpeed(); }А почему бы просто не считать шаги ШД?
Спасибо, попробую сделать через эту функцию.
Написал код используя функцию stepper.currentPosition().
#include <AccelStepper.h> #include <LiquidCrystal.h> #define button_A A0 #define button_B A1 #define button_C A2 #define button_D A3 int flag_A = 0; int flag_B = 0; int flag_C = 0; int flag_D = 0; int s,q,val = 0; float p,k = 0; long previousMillis = 0; LiquidCrystal lcd(12, 11, 10, 9, 8, 7); AccelStepper stepper(1, 3, 2); int speeds[10] = {900, 800, 700, 600, 500, 400, 300, 200, 100, 10}; float realspeeds[10] = {12.7, 11.3, 9.90, 8.48, 7.06, 5.65, 4.24, 2.83, 1.41, 0.14}; int Speed = 0; void setup() { pinMode(14, OUTPUT); pinMode(15, OUTPUT); pinMode(16, OUTPUT); pinMode(17, OUTPUT); stepper.setAcceleration(10.0); stepper.setMaxSpeed(1000); lcd.clear(); lcd.begin(16, 2); lcd.print("Speed: "); lcd.setCursor(11, 0); lcd.print("m/min"); lcd.setCursor(0, 1); lcd.print("DISTANCE: "); } void loop() { if(digitalRead(button_B) == HIGH && flag_B == 0) { Speed++; if(Speed == 10) Speed = 0; flag_B = 1; } if(digitalRead(button_B) == LOW && flag_B == 1) { flag_B = 0; } if(digitalRead(button_C) == HIGH && flag_C == 0) { Speed--; if(Speed == -1) Speed = 9; flag_C = 1; } if(digitalRead(button_C) == LOW && flag_C == 1) { flag_C = 0; } static int SpeedPrev; if(Speed != SpeedPrev) { lcd.setCursor(7, 0); lcd.print(realspeeds[Speed]); lcd.setCursor(11, 0); lcd.print("m/min"); SpeedPrev = Speed; } if(digitalRead(button_A) == HIGH && flag_A == 0) { stepper.setSpeed(speeds[Speed]); flag_A = 1; } if(digitalRead(button_A) == LOW && flag_A == 1) { flag_A = 0; } if(digitalRead(button_D) == HIGH && flag_D == 0) { stepper.setSpeed(0); flag_D = 1; } if(digitalRead(button_D) == LOW && flag_D == 1) { flag_D = 0; } if(digitalRead(button_D) == HIGH) { if (millis() - previousMillis >500) { previousMillis = millis(); val++; } } else { val=0; } if(val>=5) { k = 0; stepper.setCurrentPosition(0); val=0; } s = stepper.currentPosition(); q = s / 10; k = s / (float)200; stepper.runSpeed(); static int prev; if (q != prev) { p = (float)47.1 * k; lcd.setCursor(11, 1); lcd.print(p); prev = q; } }Вопрос появился, возможно не по теме. Как определить загруженность процессора в контроллере? Например, если каждый новый цикл выводить на дисплей любое изменение/или не изменение дистанции без остановки, то ШД заметно теряет скорость. А если еще это значение увеличивается на порядок, то скорость ШД на глазах стремится к нулю.
Нужно выводит на дисплей данные только при их изменении
Поищите тему на форуме, я топикстартер, название не помню, maksim всё объяснил.
Нужно выводит на дисплей данные только при их изменении
Это я и реализовал в своём коде.
s = stepper.currentPosition(); q = s / 10; // каждые 10 шагов будет обновляться информация на дисплее k = s / (float)200; stepper.runSpeed(); static int prev; if (q != prev) { p = (float)47.1 * k; //lcd.setCursor(11, 1); // lcd.print(p, 1); Serial.print(" _ "); Serial.println(p, 1); prev = q; }Я хочу узнать закономерность загрузки ЦП, чтобы в будущем писать код с учётом этого. Спасибо, поищу на форуме.
Ох и намудрили с удержанием...
if(!flag_D && digitalRead(button_D)) { stepper.setSpeed(0); flag_D = 1; previousMillis = millis(); } else if(flag_D && !digitalRead(button_D)) { flag_D = 0; previousMillis = 0; } else if(previousMillis && millis() - previousMillis > 2000 && digitalRead(button_D)) { k = 0; stepper.setCurrentPosition(0); previousMillis = 0; }Так как digitalRead() медленная функция, выставляйте флаги в начало условий.
Можно и я вмешаюсь в тему?
Подскажите как правильно сделать чтоб ШД сначала сделал moveTo на столько сколько установленно а потом пошла дальше программа.
Потому что при таком раскладе видно что ШД крутит с прерываниями и очень медленно.
#include <EEPROM.h> #define ENCODER_DO_NOT_USE_INTERRUPTS // без прерываний. #include <Encoder.h> #include <AccelStepper.h>// библиотека шаговиков AccelStepper Stepper1(1,54,55); //название движка и 1=по умолчанию (для драйвера 4488). 54=STEP . 55=DIR // здесь экран #include <LiquidCrystal.h> //Библиотека LCD LiquidCrystal lcd(16, 17, 23, 25, 27, 29); // переменные int dir = 1; //используется для смены направления ШД byte vozduh = 0; //переменная для турбины (0-255) int podazh = 0; // переменная для шагов вентиля подачи масла boolean alarm = true; // переменная для включения аварии int t_kotel = 0; // переменная для температуры котла int t_ulica = 0; // переменная для температуры улицы int stroka = 0;// переменная номера строчек меню long previousMillis = 0; // время последнего задействования long intervalKnopok = 700; // интервал для обработки кнопок //объявление входов-выходов Encoder knobEnkoder(31, 33); //кнопки энкодера int sign = 37; //бузер аварии, динамик (100 гц нормально пищит) int nasos = 10; // выход на твердотелое реле управление насосом int turbin = 9; //турбина подключена к 9 пину. int enter = 41; // кнопка стопа на плате int enterEnk = 35; //!!!!!!!!!!!!!!!!! кнопка на энкодере void setup() { Stepper1.setMaxSpeed(100000); //устанавливаем максимальную скорость вращения ротора двигателя (шагов/секунду) Stepper1.setAcceleration(12000); //устанавливаем ускорение (шагов/секунду^2) //Настройка пинов pinMode(enterEnk, INPUT_PULLUP); pinMode(enter, INPUT_PULLUP); pinMode(sign, OUTPUT); pinMode(nasos, OUTPUT); pinMode(turbin, OUTPUT);// выход на турбину, движек 12 вольт //Настройка дисплея //Установка количества столбцов и строк дисплея lcd.begin(20, 4); // Вывод приветствия при включении питания если нужно lcd.setCursor(6, 1); lcd.print("BUYANKA"); delay (2000);//Задержка приветствия lcd.clear(); } long positionLeft = -999; // позиция для энкодера в лево // установки для ШД void loop() { { //ОТОБРАЖЕНИЯ ТЕМПЕРАТУРЫ ВОДЫ НА ВЫХОДЕ КОТЛА lcd.setCursor(0, 0); lcd.print("KOTEL="); lcd.print(t_kotel); //отображение температуры улицы lcd.setCursor(10, 0); lcd.print("ULICA="); lcd.print(t_ulica); // турбина lcd.setCursor(0, 1); lcd.print("TURBINA="); lcd.print(vozduh); //подача масла lcd.setCursor(0, 2); lcd.print("PODAZHA="); lcd.print(podazh); // сигнализация if (alarm == true ) { lcd.setCursor(0, 3); lcd.print("SIGNAL=DA "); } else if (alarm == false) { lcd.setCursor(0, 3); lcd.print("SIGNAL=NET"); } } /////////////////////////////скрытое перелистывание переменной строчек меню нажатием на энкодер if (digitalRead(35)== LOW)// считываю состояние кнопки { unsigned long currentMillis = millis(); //запускаю отсчет if(currentMillis - previousMillis > intervalKnopok)// отсчитываю время по intervalKnopok { previousMillis = currentMillis; //сохраняю сразу время последнего включения stroka++;// строка меню на одну строчку перелистывается хотя этого и не видно if (stroka>2)// задаю чтоб прокручивлось по кругу всего из двух строчек- 1/2/1/2 { stroka=1; } } } /////////////////////////теперь обработка энкодера на изменение двух величин ////////////////////////(скорость турбины и шаги открытия вентиля) long newLeft; /// новая локальная переменная для энкодера newLeft = knobEnkoder.read()/4; // она равна /4 сигналам так как это приемливый делитель для моего энкодера. if (newLeft > positionLeft && stroka==1){ //здесь определяется направление вращения и если строка равна 1 lcd.clear(); // перед записью чищу экран vozduh ++ ; // и увеличиваю значение на один vozduh = min(vozduh, 254); // здесь делаю ограничение не больше 254, странно хотел 255 но не получилось, //перескакивает все равно на 0 positionLeft = newLeft; // запоминаю текущую позицию энкодера } if (newLeft < positionLeft && stroka==1){ // здесь все аналогично только уменьшаю значение на 1 lcd.clear(); vozduh -- ; vozduh = max(vozduh, 1); positionLeft = newLeft; } /////////////////////тоже самое но для подачи шагов для вентиля newLeft = knobEnkoder.read()/4; // она равна /4 сигналам так как это приемливый делитель для моего энкодера. if (newLeft > positionLeft && stroka==2){ //здесь определяется направление вращения и если строка равна 2 lcd.clear(); // перед записью чищу экран podazh ++ ; // и увеличиваю значение на один positionLeft = newLeft; // запоминаю текущую позицию энкодера } if (newLeft < positionLeft && stroka==2){ // здесь все аналогично только уменьшаю значение на 1 lcd.clear(); podazh -- ; podazh = max(podazh, 1); positionLeft = newLeft; } Stepper1.move(podazh * 20); //устанавливает следующее перемещение на 1600 шагов (если dir равен -1 будет перемещаться -1600 -> противоположное направление) Stepper1.run(); //запуск шагового двигателя. Эта строка повторяется вновь и вновь для непрерывного вращения двигателя /////////////// команда для сигнализации if (digitalRead(enter) == LOW){ unsigned long currentMillis = millis(); if(currentMillis - previousMillis > intervalKnopok){ previousMillis = currentMillis; alarm = !alarm; } } ///////// управление турбиной analogWrite(9, vozduh ); }Пробовал через switch...case ...
пока что наверное недорос знаниями((( не получилось