Выполнение последовательного действия в качестве прерывания

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

void jog() {
  if(input[1] == 0) sd.setDirection(0);
  else sd.setDirection(1);
  while(true){
    if(Serial.available() > 0) break;
    sd.step();
    delayMicroseconds(10);
  }
  sd.setDirection(0);
}

Прямо сейчас у меня есть это (я использую библиотеку Pololu, которая определяет шаги), но по какой-то причине это ничего не делает или, по крайней мере, движется так медленно, что незаметно (один шаг перемещает двигатель только на 0,007 градуса). Он очень плавно перемещает двигатель, если я удаляю, если(Serial.available() > 0) сломается;> но тогда я не могу его остановить.

void loop() {
  getData();
  switch(input[0]){
    case 'r':
      Serial.write('r');
      ramp();
      break;
    case 'c':
      Serial.write('c');
      cycle();
      break;
    case 'j':
      //Serial.write('j');
      jog();
      break;
    default:
      break;
  }
}
void getData(){
if(Serial.available() > 0){
  for(int i = 0; i < 200; i ++){
    input[i] = 0;
  }
  for(int i = 0; i < Serial.available(); i++){
    input[i] = Serial.read();
    Serial.print(input[i]);
  }
}
Serial.flush();
//Serial.print(input[0]);
}

Это соответствующий код, который вызывает jog()

, 👍1

Обсуждение

Пожалуйста, покажите нам полный рабочий пример, который показывает вашу проблему, а не только одну функцию. Может ли быть так, что в последовательном буфере все еще есть данные, когда вы вводите функцию jog()` В этом случае он выйдет напрямую, @chrisl

Когда он запускается, в буфере ничего нет. Сообщение обновлено с полным кодом., @Bob Dave


1 ответ


1

Как вы узнаете, что в последовательном буфере ничего нет, когда вы вводите функцию jog ()? Вы не проверяете это.

Я думаю, что ваша проблема на самом деле в вашей функции getData (). Цикл for

for(int i = 0; i < Serial.available(); i++){

может сработать не так, как вы думаете, потому что i увеличивается на каждой итерации цикла, в то время как Serial.available() уменьшается на каждой итерации. Таким образом, вы на самом деле читаете меньше данных, чем доступно в буфере. Например, предположим, что у вас есть 2 байта в буфере. Серийный.доступно() возвращает 2. В первой итерации цикла для i равно 0, в то время как Serial.available() равно 2. При чтении из последовательного буфера в итерации значение Serial.available() уменьшается до 1 (так как вы удалили один байт из буфера, прочитав его). В конце итерации цикл for увеличит i на 1 (из-за i++). Теперь в состоянии вашего цикла for у вас есть 1 < 1 (потому что и i, и Serial.available() равны 1), таким образом, цикл for завершится без каких-либо дальнейших итераций. Но у вас все еще остался 1 байт в буфере, который вы не прочитали.

Как я читал из документации Serial.flush() (хотя документация, как известно, плохая) и из этого вопроса, я вижу, что она будет ждать только до тех пор, пока выходной буфер не будет очищен подпрограммами прерывания. Он ничего не делает с входным буфером. Таким образом, у вас все еще есть данные в буфере RX после этой строки.

Это означает, что у вас действительно есть данные в буфере RX, когда вы входите в функцию jog (). Таким образом, он выйдет напрямую, не делая ни одного шага.


Чтобы решить эту проблему, вы должны сначала исправить свою функцию getData (), чтобы прочитать все доступные данные из буфера RX. Вы можете изменить свой цикл for следующим образом:

for(int i = 0; Serial.available() > 0 && i < INPUT_BUFFER_SIZE; i++){

Это будет повторяться до тех пор, пока буфер RX не опустеет (когда Serial.available() возвращает 0) или пока ваш входной массив[] не заполнится (пожалуйста, добавьте константу или определите размер входного буфера. Может быть, он у вас уже есть. Вы не включили полный код. Но я думаю, что это 200).

После цикла for вы можете очистить буфер, прочитав из него, пока ничего не останется (пожалуйста, смотрите Связанный вопрос для этого). Вы даже можете сделать это в начале своей функции jog (). Это гарантирует, в основном, что в буфере нет данных в начале функции jog () (в основном, потому что время приема данных может быть таким, что вы получите байт между опорожнением буфера RX и циклом. Маловероятно, но не исключено).

Хотя, как утверждает Ник Гэммон в связанном ответе, это не лучший способ очистить буфер RX и использовать каждый последовательный ввод в качестве прерывания (иногда у вас нет точного контроля, когда именно поступают данные). Лучшим решением является реализация протокола сообщений, в котором конец полного сообщения помечается специальным символом. Затем вы читаете с последовательного, пока не будет получено полное сообщение (вы даже можете сделать это неблокирующим, чтобы вы могли делать другие вещи, пока сообщение не будет завершено), и только тогда действуйте в соответствии с сообщением. Для этого потребуются большие изменения в вашем коде. Вам придется переписать свою функцию getData (), а затем удалить весь блокирующий код из скетча (например, все задержки) и вместо этого работать с неблокирующим кодом (например, с помощью функции micros (). Принцип объясняется в примере BlinkWithoutDelay, который поставляется с IDE Arduino (хотя он использует millis() вместо micros (), это тот же принцип). Вы бы неоднократно вызывали функцию jog (), которая выполняла бы только один шаг, если бы пришло время его выполнить. Тем временем остальная часть кода может получать новые сообщения. Это делает вас более гибким и независимым от точного времени приема данных. Поскольку это было бы большим переписыванием вашего кода, вы должны сами решить, хотите ли вы пойти этим путем или остаться с более простым и несовершенным решением.

,