Я пытаюсь управлять двумя шаговыми двигателями с помощью последовательного ввода. Мой код работает, однако мне приходится несколько раз нажимать новую команду направления

Я использую два шаговых двигателя 28BYJ-48 с двумя драйверами, которые идут в комплекте. (Я не знаю их названия.)

Вот мой код:

/*
* Код для одновременного управления двумя шаговыми двигателями с использованием вызовов функций библиотеки шаговых двигателей
* Этот код имеет функции, которые делают вызовы для запуска одного шага от каждого из двух двигателей за раз.
* поэтому каждый двигатель по очереди делает шаг, в результате чего они оба работают вместе
* переменная 'lastCall' обеспечивает непрерывный ввод, чтобы поддерживать работу степперов, если нет последовательного ввода
* Написано Габриэлем Адамсом — март 2023 г. —
*/
#include <Stepper.h>

//определения для каждой команды, которая будет получена через последовательный порт
#define COMMAND_LEFT 'a'
#define COMMAND_RIGHT 'd'
#define COMMAND_FORWARD 'w'
#define COMMAND_BACK 's'
#define COMMAND_STOP ' '

//Количество шагов на внутренний оборот двигателя
const float STEPS_PER_REV = 32;

//Величина понижения передачи
const float GEAR_RED = 64;

//Количество шагов на один оборот выходного редуктора
const float STEPS_PER_OUT_REV = STEPS_PER_REV * GEAR_RED;

//введите здесь количество шагов на оборот для ваших двигателей
int stepsInRev = STEPS_PER_OUT_REV ;

// это устанавливает значение для циклов for и, следовательно, устанавливает количество шагов в каждом вызове
int num_of_steps = 1;
// установка выводов для каждого драйвера Motor1 ~ IN1, IN2, IN3, IN4; двигатель2 ~ IN1, IN2, IN3, IN4
Stepper myStepper1(STEPS_PER_OUT_REV, 4, 6, 5, 7);
Stepper myStepper2(STEPS_PER_OUT_REV, 8, 10, 9, 11);
// переменная для хранения последнего обращения к последовательному порту
char lastCall = ' ';

// Я прототипировал здесь все функции, но по какой-то причине Arduino IDE это не понравилось
//поэтому они закодированы, пока я не выясню, почему
//аннулировать forwardStep();
//отменить обратный шаг();
// недействительный левый шаг();
// недействительный правый шаг();
//аннулировать allStop();

//двигать моторы вперед
void forwardStep(int steps){
  Serial.println("forward");
  // шаг вперед на один шаг
  myStepper1.step(1);
  myStepper2.step(1);
  delay(10);
}

// чтобы переместить моторы назад
void backwardStep(int steps){
  Serial.println("backward");
  // шаг на один шаг назад
  myStepper1.step(-1);
  myStepper2.step(-1);
  delay(10);
}
 

// для перемещения двигателей в противоположных направлениях (слева)
void leftStep(int steps){
  Serial.println("left");
 // шаг на один шаг влево
  myStepper1.step(1);
  myStepper2.step(-1);
  delay(10);
}


// для перемещения двигателей в противоположных направлениях (справа)
void rightStep(int steps){
  Serial.println("right");
  // шаг на один шаг вправо
  myStepper1.step(-1);
  myStepper2.step(1);
  delay(10); 
}

// чтобы отключить драйверы моторов и остановить моторы
void allStop(){
  Serial.println("stop");
  // шаговики останавливаются
  PORTD = B00000000; //устанавливает все контакты с 0 по 7 в состояние LOW, чтобы выключить stepper1
  PORTB = B00000000; //устанавливает все контакты с 8 по 13 в состояние LOW, чтобы выключить stepper2
}
void setup() {
  Serial.begin(9600);//запускаем последовательный порт bluetooth - отправка и получение на скорости 9600 бод
  // устанавливаем скорость 60 об/мин:
  myStepper1.setSpeed(700);
  myStepper2.setSpeed(700);
}

void loop() {
//проверить, есть ли последовательная связь, и если да, прочитать данные
if(Serial.available()) {
char data = (char)Serial.read();
// переключиться на установку символа через последовательный порт на команду
switch(data) {
  case COMMAND_FORWARD:
    forwardStep(num_of_steps);
    break; 
  case COMMAND_BACK:
    backwardStep(num_of_steps);
    break;
  case COMMAND_LEFT:
    leftStep(num_of_steps);
    break;
  case COMMAND_RIGHT:
    rightStep(num_of_steps);
    break;
  case COMMAND_STOP:
    allStop();
    break;
}
// установить переменную 'lastCall' на последний вызов из серийного номера
lastCall = data;
}

char data = lastCall;

Loop(lastCall);
Serial.read();

}

void Loop(char x){
switch(lastCall) {
  case COMMAND_FORWARD:
    forwardStep(num_of_steps);
    break; 
  case COMMAND_BACK:
    backwardStep(num_of_steps);
    break;
  case COMMAND_LEFT:
    leftStep(num_of_steps);
    break;
  case COMMAND_RIGHT:
    rightStep(num_of_steps);
    break;
  case COMMAND_STOP:
    allStop();
    break;
}
return lastCall; 

(char)Serial.read();
}

, 👍0

Обсуждение

Почему вы делаете эти два Serial.read(), не используя результат за пределами вашего последовательного кода? Если ваши данные поступят прямо перед этим, они будут просто удалены., @chrisl

Я все еще нахожусь в процессе изучения объектно-ориентированного кодирования. Я думал, что команда Serial.read() обеспечит более быстрое чтение последовательных входов. Как мне лучше оценить это? Например, вместо того, как в данный момент он не изменит направление немедленно, когда я даю новую команду, как мне использовать команду Serial.read() для быстрого перехода к новому циклу., @Gabriel Adams

Кажется, у вас странное понимание параметров функции, которые не используются функциями. Кстати: прототипирование функций выполняется за вас IDE, но если вы делаете это самостоятельно, делайте это правильно: параметры должны подходить., @DataFiddler


1 ответ


1

Проблема в том, что вы читаете из последовательного буфера без использования данных. В вашем коде есть три места, где вы используете Serial.read();:

  1. Вы используете in в блоке с серийным кодом и соответствующим образом устанавливаете lastCall. Это нормально.
  2. Затем вы используете его в конце void loop(), но ничему не присваиваете значение.
  3. И вы используете его в конце своей собственной функции Loop(), снова не присваивая значение чему-либо.

Краткое объяснение того, как работает Последовательный: Uno имеет аппаратное обеспечение для обработки последовательной связи в фоновом режиме через прерывания. Данные поступают на Uno и автоматически (в фоновом режиме) помещаются во внутренний буфер объекта Serial. Вызов Serial.read() удаляет самый старый байт из буфера (чтобы вы получали байты в порядке поступления) и возвращает его. Если нет доступных новых данных, он вернет -1.

Поэтому, когда вы просто используете Serial.read(), не присваивая значение чему-либо, вы фактически выбрасываете байты данных из последовательного буфера.

Я думал, что команда Serial.read() обеспечит более быстрое чтение последовательного ввода.

Чтения данных из буфера недостаточно. На самом деле нужно что-то с этим делать. В противном случае вы просто читаете, не реагируя. Поэтому удалите два вызова Serial.read(), оставив только один в блоке if(Serial.available()).

почему в данный момент он не изменит сразу направление, когда я даю новую команду, как мне использовать команду Serial.read() для быстрого перехода к новому циклу?

Вы должны удалить все вызовы delay(). В настоящее время ваш код тратит больше всего времени на эти задержки, ничего не делая. Вместо этого вы можете использовать неблокирующий стиль кодирования из примера BlinkWithoutDelay, который поставляется с Arduino IDE. Он использует millis() в качестве хранителя времени, выполняя код (мигание светодиода в примере; выполнение шага в вашем коде) только тогда, когда пришло время это сделать. В Интернете есть много руководств по этому поводу.

Если вы хотите, вы можете пойти еще дальше и переключиться на библиотеку AccelStepper, которая имеет больше возможностей, чем стандартная библиотека Stepper. Он позволяет вам установить скорость и ускорение, а затем вы просто запускаете двигатель, достаточно часто выполняя метод run(). Тогда нет необходимости в собственном времени шагов, потому что библиотека справляется с этим. Хотя это будет более серьезное изменение в вашем коде.

,