Arduino MEGA — Как отправлять и получать данные асинхронно?

я новичок в этом, поэтому надеюсь, что смогу понятно объяснить свою проблему;

Arduino MEGA подключен к ПК через порт USB.

Я использую кодировщик для запуска функции отправки данных на компьютер, данные представляют собой простую строку "A01" или "А02" когда я поворачиваю энкодер в противоположном направлении, когда arduino отправляет эту строку в последовательный порт, ПК отвечает другой строкой из последовательного порта, и это что-то вроде «MCPHDGxxx»; и arduino делает, если серийный номер доступен, получает строку и фильтрует ее, чтобы получить и отобразить это число на моем 7-сегментном дисплее. (xxx — это число от 000 до 359, и это число меняется на экране ПК, когда Arduino отправляет строку «A01» или «A02» на ПК)

Когда я не отправляю данные дисплея с ПК на Arduino, функция кодировщика работает должным образом, быстро меняет числа вверх и вниз на экране ПК, но когда я пытаюсь отправить это "число" данные в Arduino с номера последовательного порта на экране ПК изменяются так медленно, как 1 точка вверх/вниз за 2 секунды, даже если я постоянно поворачиваю энкодер. Похоже, что получение данных с ПК блокирует мою функцию отправки данных, пока это не будет выполнено.

Можно ли заставить его работать асинхронно?

Вот мой набросок:

#include "DigitLedDisplay.h"
 #define outputA 24
 #define outputB 25

 int aState;
 int aLastState;  
 unsigned long lastButtonPress = 0;
 long finalMCPHDGLong;
 String ReceivedSerialString;

 DigitLedDisplay ld = DigitLedDisplay(21, 22, 23);

 void setup() { 
   pinMode (outputA,INPUT);
   pinMode (outputB,INPUT);
   
   Serial.begin (115200);
   // Считывает начальное состояние outputA
   aLastState = digitalRead(outputA);

     /* Set the brightness min:1, max:15 */
  ld.setBright(10);

  /* Set the digit count */
  ld.setDigitLimit(8);

    
 } 

 void loop() { 
   aState = digitalRead(outputA); // Считывает "текущий" состояние выходаA
   // Если предыдущее и текущее состояние выхода A отличаются, это означает, что произошел Pulse
   if (aState != aLastState){     
     // Если состояние outputB отличается от состояния outputA, это означает, что энкодер вращается по часовой стрелке
     if (digitalRead(outputB) != aState) {
       if (millis() - lastButtonPress > 25) {
         Serial.println("A01");
        }

        // Запоминаем событие последнего нажатия кнопки
        lastButtonPress = millis(); 
       
     } else {
       if (millis() - lastButtonPress > 25) {
         Serial.println("A02");
        }

        // Запоминаем событие последнего нажатия кнопки
          lastButtonPress = millis();
       
     }
     
   } 
   aLastState = aState; 

    while (Serial.available() > 0){
      ReceivedSerialString = Serial.readString();
      if (ReceivedSerialString.substring(0,6) == "MCPHDG"){
        String value = ReceivedSerialString.substring(6,9);
          long val = value.toInt();
          finalMCPHDGLong = val;
          if((finalMCPHDGLong <= 99)){
              ld.printDigit(0,7);
            }if((finalMCPHDGLong <= 9)){
              ld.printDigit(0,6);
            }
          ld.printDigit(val, 5);
      }
    }
 }

, 👍1


2 ответа


Лучший ответ:

2

Похоже, получение данных с ПК блокирует мою функцию отправки данных, пока это не будет выполнено.

Возможно, вы говорите то же самое, но я бы сказал, что он ожидает поступления дополнительных данных, которых не будет.

Можно ли заставить его работать асинхронно?

В зависимости от того, как вы определяете "асинхронно" это может быть ненужным.

while (Serial.available() > 0){
  if (ReceivedSerialString.substring(0,6) == "MCPHDG"){
    String value = ReceivedSerialString.substring(6,9);

Можно многое сказать об использовании String и преобразовании в int, выборе простого формата сообщения и так далее, но большую часть этого я опускаю. Поиск хорошего подхода к тому, что вы делаете, потребует гораздо большего понимания того, что вы делаете и почему. Но, принимая ваш код и вопрос в том виде, в котором они написаны, главное заключается в том, что readString() здесь завершается на основе значения Serial.setTimeout(), которое по умолчанию 1000 или 1 секунда. Таким образом, вы тратите много времени, ничего не делая после получения вашей команды от ПК. Таким образом, в течение этой 1 секунды вы не работаете с "энкодером".

Вы можете поиграть с setTimeout, но было бы лучше вообще избежать тайм-аута, не пытаясь читать, когда больше нет команды для чтения. Если этот протокол, который вы используете, является линейно-ориентированным, то у вас есть терминатор, который вы можете использовать в форме '\r' или '\n'. Это можно сделать с помощью Serial.readStringUntil(whatever_your_terminator_is); Если у вас есть и '\r', и '\n', вам нужно будет прочитать другую часть последовательности завершения, а затем отбросить ее. Либо он войдет как часть прочитанной строки, либо нет, и вам нужно будет выполнить второе чтение, чтобы отбросить его. Есть более сложные способы сделать это, но этого может быть достаточно для того, что вы делаете.

Чтобы не блокировать (или блокировать в значительной степени) последовательное чтение, вы должны убедиться, что данные доступны для чтения. Либо потому, что вы знаете, что оно отправлено (и готовы верить, что оно было получено нормально). Или вы используете Serial.setTimeout с 0 или достаточно маленьким числом, .read() указывает на отсутствие данных с отрицательным возвращаемым значением или проверяет .available( ), и все эти случаи решают, что делать, когда вы еще не все получили или не все... еще. Делать это вручную вручную довольно сложно.

Для записи/печати в серийный номер есть аналог .availableForWrite(), который вы можете использовать, чтобы заранее определить, застрянете ли вы в ожидании (в .print()/.println()/. write()), чтобы последовательные данные выходили, в свою очередь, не обращаясь к энкодеру. По сути, вы можете выбрать, где и как ограничить запись в последовательный порт, чтобы не слишком мешать проверке кодировщика, а не вызывать дросселирование внутри Serial.write. С тем, как ваш код написан в настоящее время, с событиями, поступающими с той скоростью, с которой они, по-видимому, (согласно вашему коду устранения дребезга), это, вероятно, в настоящее время не является проблемой для вас. Но об этом стоит помнить, если вы начинаете обрабатывать события с большей скоростью. Размещение символа в исходящем последовательном буфере выполняется значительно быстрее, чем ожидание появления свободного места в последовательном буфере.

,

Serial.setTimeout(25) решил проблему. Также очень помогли другие советы, спасибо., @Nocturnale


-1

Это непросто, но можно реализовать две модификации.

Первый из них обрабатывает энкодер через прерывание, второй — это изменение while на if, при этом while вы заставляете код зацикливаться внутри последовательного интерфейса while, что в конечном итоге приводит к потере состояния энкодеров, чтобы избежать этого, вы должны постоянно берите строки, поступающие с ПК, и создавайте очередь команд, и обрабатывайте ее по циклу, к сожалению, arduino mega - это один процессор, поэтому он не может обрабатывать разные процессы одновременно.

Вы также должны рассмотреть и запустить эталонный тест, чтобы проверить, насколько высока частота обновления вашего дисплея

,