Отправка данных аналогового датчика на ноутбук со скоростью 1 кГц через USB

Я читаю данные аналогового датчика с помощью Arduino nano, и требуемая частота дискретизации составляет около 1 кГц. Я использую пример AnalogReadSerial, предоставленный Arduino с задержкой 1 мс, поэтому я ожидаю получить около 1000 выборок в секунду или чуть меньше из-за задержки преобразования АЦП. Однако максимальное количество сэмплов, которые я могу получить в своем ноутбуке, чуть меньше 200 сэмплов в секунду. Я попытался увеличить скорость передачи данных до 115200, но это не помогло. Я что-то упускаю здесь, или мне нужно что-то сделать, чтобы получить мои данные с требуемой частотой дискретизации?

Ниже приведен код AnalogReadSerial, который я использовал для справки:

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(115200);
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // print out the value you read:
  Serial.println(sensorValue);
  delay(1);        // delay in between reads for stability
}

, 👍1

Обсуждение

запишите значение millis() до и после int sensorValue = analogRead(A0); ... затем выведите два значения, @jsotola

@jsotola Я использовал micros(), и это занимает 116 микросекунд., @AmrSherbiny

115kBaud на самом деле должно быть достаточно. 1000 сообщений примерно по 6 байт каждое (4-значное число и CR/LF) используют 6000 байт или 60000 бит в секунду. Как быстро вы сможете это сделать, если уберете задержку?, @PMF

Запуск вашего кода на моем Uno дает мне 791,5 сэмплов/с. Может быть, ваш ноутбук слишком медленный, чтобы справиться со скоростью передачи данных?, @Edgar Bonet

Ошибка новичка от меня, мне пришлось соответствующим образом настроить скорость передачи данных USB-порта моего ноутбука, используя "ssty", потому что по умолчанию было 9600. Прямо сейчас я получаю данные со скоростью, аналогичной вашей @Edgar Bonet, которая лучше, но все еще немного стесняется требуемой скорости. @PMF ваши расчеты имеют смысл, и эта скорость должна быть в пределах моего ноутбука и Arduino nano, что все еще сбивает меня с толку. Я удалю задержку и проверю, имеет ли это какое-то значение., @AmrSherbiny

поскольку serial использует ascii, может быть меньше данных для отправки дельты между каждым чтением, чем целое число. например, 128,129,127 - это 11 символов, 128,1, -2 - это только 8 символов., @dandavis


1 ответ


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

2

Как я уже сказал в комментарии, ваш код на Uno дает мне 791,5 образцов/с. Это примерно 1263 мкс на образец:

  • около 110 мкс для analogRead(), включая 104 мкс для собственно преобразования
  • предположительно 1000 мкс для задержки(1)
  • 153 мкс для остальной части кода, в основном форматирование sensorValue в ASCII

Согласно этим таймингам, если бы не вызов delay(), Arduino смог бы завершить цикл за одну миллисекунду и все еще иметь в запасе 737 мкс. Самый простой способ добиться этого- следовать стратегии Blink Without Delay:

void loop() {
    static uint32_t last_conversion_time = micros();
    if (micros() - last_conversion_time >= 1000) {
        last_conversion_time += 1000;
        Serial.println(analogRead(A0));
    }
}

Это дает мне 1000 выборок в секунду. Обратите внимание, что last_conversion_time обновляется путем добавления 1000 мкс, а не установки его в micros (), так что на самом деле это вариация мигания Стратегия без промедления. Обновление last_conversion_time таким образом гарантирует, что всякий раз, когда программа опаздывает и пропускает нужную микросекунду, небольшие ошибки синхронизации не накапливаются.

Для максимально возможной точности синхронизации было бы более целесообразно запускать АЦП по таймеру, но этот подход более сложен и требует тщательного чтения спецификации микроконтроллера.

,

Я бы не назвал это стратегией BlinkWithoutDelay. Это совсем другое дело. Было бы лучше инициализировать last_conversion_time с помощью micros (), чтобы ему не приходилось догонять 1 мс приращения времени, потерянного в setup() ., @Juraj

@Juraj: Я отредактировал ответ, чтобы учесть ваши комментарии. Обычно я предпочитаю, чтобы last_conversion_time был инициализирован нулем, если только setup() не работает очень медленно (не в данном случае): это экономит стоимость охранной переменной и теста на каждой итерации цикла. Но тогда, может быть, я слишком склонен к микрооптимизации..., @Edgar Bonet