Что является более быстрой альтернативой parseInt()?

Я делаю светодиодный анализатор спектра с помощью Arduino Due и своего КОМПЬЮТЕРА. Обработка звука производится на ПК, а затем отправляется на Arduino. Точные передаваемые данные-это "координата", или, точнее, число, представляющее определенный диапазон, и другое число, представляющее максимальную амплитуду этого диапазона. Есть 11 полос. Каждая координата имеет значения x и y, разделенные запятой, а затем отправляется на новую строку.

Вот мой код на данный момент:

void loop() {
  if (Serial.available()) {
    xCoord = Serial.parseInt();
    yCoord = Serial.parseInt();
    if (xCoord != 0) {
      int r, g, b;
      for (int i = 0; i < 8; i++) {
        int fromCoordToIndex = ((xCoord - 1) * 8) + i;
        //
        //do some calculations for the color
        //
        strip.setPixelColor(fromCoordToIndex, strip.Color(r, g, b));
        strip.show();
      }
    }
  }
}

Проблема в том, что parseInt кажется очень медленным (это не проблема тайм-аута, он установлен на 1). Из того, что я вижу, кажется, что он пропускает некоторые данные, поэтому время от времени группа пропускается. Единственный способ исправить это-вставить задержку между каждой отправляемой координатой. Я обнаружил, что 40 мс работает нормально, но помните, что есть 11 полос, так что это означает примерно полсекунды, чтобы обновить весь борд, который выглядит как слайд-шоу...

Я пробовал вводить отдельные символы , а затем использовать toInt, я пробовал parseInt очевидно, я пробовал родной USB (это из-за), и я пробовал возиться со скоростью передачи данных. Ни один из них, похоже, не произвел никакого эффекта.

, 👍1

Обсуждение

Посылаете ли вы какой-то разделительный символ между вашими номерами?, @Edgar Bonet

@LookAlterno он не жесткий, но потребуется 2 секунды, чтобы сделать его жестким,формат x, y (включая запятую в качестве разделителя, и он отправляется в виде строки), @mr-matt

Я не понимаю, что вы имеете в виду, @mr-matt

Гораздо более быстрая альтернатива - вообще не требовать синтаксического анализа. Вместо этого отправляйте двоичные данные., @Ignacio Vazquez-Abrams

Но разве это не выходит как строка, мне все равно придется разобрать ее, чтобы использовать, не так ли?, @mr-matt

И у меня есть некоторые другие вычисления, удаленные сверху, смогу ли я все еще сделать это, если они будут двоичными?, @mr-matt

MatthewInglis вы должны указать формат. Вот с чего надо начать. Какое число может быть байтом, какие числа больше, могут ли они быть отрицательными и так далее. Как только вы это укажете, превратите его в фиксированный формат/ridig, как пытается вам сказать @LookAlterno. После этого вы можете добавить контрольную сумму или STX и ETX (начальные и конечные маркеры). Или укажите, что в конце всегда есть LineFeed или CarriageReturn. Но сначала укажите формат, отредактируйте свой вопрос и добавьте его к нему., @Jot

Более вероятно, что те вычисления, которые вы делаете после получения данных, являются медленной частью. Операции с плавающей запятой довольно медленные на arduino из-за отсутствия оборудования с плавающей запятой вместо этого предпочитают вычисления с фиксированной точкой только с использованием интегральных типов., @ratchet freak

@ratchetfreak да, конечно, и тайм-аут в 1 мс заставит parseInt прерваться (в зависимости от скорости передачи данных, которую мы не знаем). Но сначала мы должны определить протокол последовательных данных., @Jot

Первый совет-прочитать все данные перед обработкой и отображением. Также избегайте отправки дополнительных данных до завершения предыдущего. Во-вторых, проверить должную аппаратную последовательную реализацию, что она на самом деле управляется прерываниями и буферами., @Mikael Patel

Можно задать этот вопрос где-нибудь в другом месте, но не пытайтесь скрыть это от нас. Вы должны были дать ссылку на http://forum.arduino.cc/index.php?topic=508343.0, @Jot

переместите ' strip.show();` из цикла для большего ускорения, чем накладные расходы на преобразование типов, @dandavis

@dandavis хорошая мысль, спасибо, я должен был это видеть..., @mr-matt


6 ответов


0

Преждевременная оптимизация-корень всего зла (или, по крайней мере, большей его части) в программировании. Дональд Кнут.

Я не знаю, насколько быстра/медленна функция parseInt (). Как и ты. Сначала ты должен засечь время.

ХОРОШО. Может быть, это действительно медленно, но является ли это самой медленной частью вашего кода? Медленно по сравнению с чем?

Является ли синтаксический анализ тем, что вам нужно? Синтаксический анализ не преобразует отрицательные значения и может затянуться (но все равно возвращает ноль ... Да!).

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

Этот скетч показывает, как обрабатывать ваши данные:

void setup() {
  Serial.begin(9600);
  while(!Serial);

  String lines[] = {
    "1,1",
    "10,20",
    "11,1000",
  };
  int channel;
  int value;

  for (int i=0; i < 3; i++) {      
    sscanf(lines[i].c_str(), "%d,%d", &channel, &value);
    Serial.print("channel="); Serial.print(channel);
    Serial.print(" value="); Serial.println(value);
  }
}

void loop() { 
}

Ну, я использую sscanf, это, конечно, медленнее, чем синтаксический анализ, но он выполняет свою работу. Я сомневаюсь, что вы (или я) сможете сделать это лучше, и есть более полезная вещь, чтобы провести наше время.

Изменить:

Я профилировал

sscanf("0001,0001", "%d,%d", &channel, &value);

с помощью Arduino UNO и потребовалось 147 долларов, чтобы преобразовать один номинал значений.

,

0

Вместо того, чтобы использовать parseint, отправьте данные для 11 каналов в очень специфичной форме в Arduino, чтобы Arduino мог очень легко "разобрать" их.

Возможно, вместо отправки текста отправьте 33 байта для RGB (3), 11 полос-это уровни (0-255) последовательных каналов. Чем нет необходимости в преобразовании (за исключением считывания байтов).

,

Ну, мне не нужно отправлять цвет, мне просто нужно знать, какой высоты должна быть каждая полоса. Так что посылать дополнительные ценности было бы просто бессмысленно., @mr-matt

В этом случае будет достаточно 11 байтов, по одному значению для каждой полосы., @Michel Keijzers

Извините мое невежество, но что именно вы подразумеваете под байтом? Если у каждого канала есть число между (и в том числе) 0 и 13, как мне представить его в виде байта?, @mr-matt

Байт-это 8 бит (8 нулей или единиц). С помощью байта (8 бит) вы можете хранить число от 0 до 255 (или 256 различных значений). Вы даже можете отправить 2 значения от 0-13 в одном байте, поместив путем вычисления значение 1 * 13 + значение 2 (которое всегда будет меньше 256). Или более соответствующих битов: значение 1 << 4 + значение 2... То << оператор сдвигает биты на четыре влево, что равно умножению на 16. Значение 1 будет в четырех битах слева (MSB), а значение 2-в четырех битах справа (LSB). Поэтому, если ваша система изменится на 16 каналов, вам не нужно ничего менять (по сравнению с 11 каналами, использующими это)., @Michel Keijzers

Хорошо, итак, если я отправлю "025" в arduino с последовательным интерфейсом, могу ли я использовать " serial.readBytes ()"? И если я это сделаю, будет ли преобразование его в целое число медленным, как из строки?, @mr-matt

Ну, вы можете лучше не отправлять "025", потому что это 3 символа (байта). Вы можете отправить, например, первые два по 0 * 16 + 2 = 2... поэтому отправьте байт со значением 2 (это не символ 2)., @Michel Keijzers


2

Раздражает в функции parseInt() не ее скорость, а тот факт , что она зависит от времени ожидания. Если время ожидания слишком велико, это замедляет вашу работу. Если он слишком короткий, это может привести к ошибкам.

Более безопасный способ анализа сообщений, которые вы используете, - это буферизировать все входящие символы до тех пор, пока вы не найдете возврат каретки, а затем проанализировать всю строку. Например:

// Просто повторите сообщения с другим разделителем.
void parse(char *buffer)
{
    char *s = strtok(buffer, ",");
    int x = atoi(s);
    s = strtok(NULL, ",");
    int y = atoi(s);
    Serial.print(x);
    Serial.print(':');
    Serial.println(y);
}

void loop()
{
    static char buffer[BUFFER_SZ];
    static size_t lg = 0;
    while (Serial.available()) {
        char c = Serial.read();
        if (c == '\r') {        // возврат каретки
            buffer[lg] = '\0';  // завершите строку
            parse(buffer);
            lg = 0;             // приготовьтесь к следующему сообщению
        }
        else if (lg < BUFFER_SZ - 1) {
            buffer[lg++] = c;
        }
    }
}

Обратите внимание, что вам следует добавить некоторую проверку ошибок при вызове strtok().

,

0

Ваши данные содержат 11 каналов и одно значение для каждого канала. Значения будут представлены в графическом формате, поэтому он укладывается в диапазон 0-255 без ухудшения представления. Вы всегда можете масштабировать показания в соответствии с разрешением дисплея.

Затем вы отправляете эти 11 значений в двоичной форме. Один байт на канал плюс один байт для отделения одного набора данных от другого. Вам не нужно отправлять номер канала, только значения.

На конце Arduino вы считываете 12 байт, а затем проходите через него, преобразуя байты в ints.

#define CHANNELS 11

void setup()
{
  Serial.begin(9600);

  while(!Serial);
}

void loop()
{
  char packet[CHANNELS + 1];
  int values[CHANNELS];

  // Считывание 12 байт (11 байт данных + разделитель 1 байт)
  int nBytes = Serial.readBytes(packet, sizeof(CHANNELS + 1));

  if(nBytes == CHANNELS + 1) {
    // Преобразование байтов в целые;
    for(int i=0; i<CHANNELS; i++) {
      values[i] = packet[i];
    }
  } else {
     // Ошибка. 
     }

  // Здесь вы показываете свой график.
}

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

,

Интересная проблема с этим, nBytes никогда не будет 11+1, когда я тестировал его, всегда было бы 4, затем 4, а затем 4, а не 12 за один раз. Есть какие-нибудь идеи?, @mr-matt


0

Я, кажется, исправил это, снизив скорость передачи данных со 115200 до 9600. Этот ответ вдохновил на перемены. По-видимому, чем выше скорость передачи, тем быстрее передается информация, но чем выше вероятность ошибки, и чем ниже скорость передачи, тем медленнее она передается, но тем она надежнее.

,

1
#include <SoftwareSerial>
SoftwareSerial s1(5,6);

void setup {
s1.begin(9600);
s1.setTimeout(50); //sets the timeout of parseint() to 50ms

}

Пожалуйста, обратите внимание, что слишком большое снижение таймаута может привести к ошибкам, я бы не стал опускаться ниже 10.

,