Що таке більш швидка альтернатива 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 (це Due) і я намагався маніпуляції зі швидкістю обміну даними. Здається, ніхто не мав жодного ефекту.

1
Більш імовірно, що ті обчислення, які ви робите після отримання даних, є повільною частиною. Операції з плаваючою точкою досить повільні на ардуїно через відсутність апаратного забезпечення з плаваючою точкою, замість цього використовують виключення з фіксованою точкою лише за допомогою цілісних типів.
додано Автор CTKeane, джерело
перемістіть strip.show (); з циклу для більшого прискорення, ніж накладні перетворення типу
додано Автор Alastair, джерело
Чи відправляєте ви певну форму роздільника між номерами?
додано Автор Sprogz, джерело
Набагато швидша альтернатива - не вимагати розбору. Натомість надсилайте двійкові дані.
додано Автор Ignacio Vazquez-Abrams, джерело
@LookAlterno не жорсткий, але займе 2 секунди, щоб зробити його жорстким, формат x, y (включаючи кому як роздільник, і він посилається як рядок)
додано Автор xandriksson, джерело
Я не розумію, що ви маєте на увазі
додано Автор xandriksson, джерело
Але чи не вийшло це як рядок, мені все одно доведеться розібрати його, щоб використовувати його?
додано Автор xandriksson, джерело
І у мене є деякі інші розрахунки, щоб зробити видалені зверху, я все ще буду в змозі зробити це, якщо вони двійкові?
додано Автор xandriksson, джерело
@ dandavis хороший момент, спасибі, я повинен був бачити, що ...
додано Автор xandriksson, джерело
Перший натяк - прочитати всі дані перед обробкою та відображенням. Також уникайте надсилання додаткових даних перед завершенням попереднього. По-друге, перевірити послідовну реалізацію апаратного забезпечення Due, що воно є керованим перериванням і буферами.
додано Автор Mikael Patel, джерело
Ви можете зробити більш швидкий parseInt , якщо ваші дані мають жорсткий формат (наприклад, завжди три цифри довжиною, додаються нулі або пробіли. Повідомте нам, якщо це так (і який формат використовується) .
додано Автор user31481, джерело
Наприклад, всі значення є позитивними 3 цифрами (додаються до нуля), і вони приходять парами, розділеними комами, наприклад, 003,456 ... 450,001 ...
додано Автор user31481, джерело
Розмістіть деталі свого формату, щоб забити запитання.
додано Автор user31481, джерело
To Це нормально ставити це питання в іншому місці, але не намагайтеся приховати це від нас. Ви повинні були надати посилання на
додано Автор Standback, джерело
@ratchetfreak так, звичайно, і тайм-аут 1ms зробить parseInt abort (залежно від baudrate, який ми не знаємо). Але спочатку треба визначити протокол послідовних даних.
додано Автор Standback, джерело
MatthewInglis вам потрібно вказати формат. Ось з чого почати. Яке число може бути байтом, числа якого більше, чи можуть вони бути негативними і так далі. Після того, як ви вказали це, перетворіть його у формат фіксованого/виграшного формату, який @LookAlterno намагається розповісти. Після цього можна додати контрольну суму, або STX і ETX (початковий і кінцевий маркери). Або вкажіть, що в кінці завжди існує LineFeed або CarriageReturn. Але спершу вкажіть формат, відредагуйте питання і додайте його.
додано Автор Standback, джерело

5 Відповіді

Дратівна річ про parseInt() - це не її швидкість, це факт що він спирається на тайм-аут. Якщо час очікування занадто довгий, це уповільнює роботу вниз. Якщо він занадто короткий, він може викликати помилки.

Більш безпечний спосіб розбору типів повідомлень, які ви використовуєте, полягає в буферизації всіх вхідних символів, поки не знайдете повернення каретки, а потім розібрати цілу лінію. Наприклад:

// Just echo the messages with a different separator.
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') {       //carriage return
            buffer[lg] = '\0'; //terminate the string
            parse(buffer);
            lg = 0;            //get ready for next message
        }
        else if (lg < BUFFER_SZ - 1) {
            buffer[lg++] = c;
        }
    }
}

Зауважте, що ви повинні додати певну перевірку помилок під час виклику strtok() .

3
додано

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

Я не знаю, як швидко/повільно parseInt() є. Ні ви. Ви повинні вчасно спочатку це зробити.

В ПОРЯДКУ. Можливо, це дуже повільно, але це найповільніша частина коду? Повільно в порівнянні з чим?

Чи потрібно вам parseInt ? parseInt не конвертує негативні значення, і він може вичерпати час (але все одно повертає нуль ... Duh!).

Якщо ви прочитаєте весь рядок як рядок, а потім розібрати значення, ви уникнете проблеми з тайм-аутом, а також можете перевірити правильність даних.

Цей ескіз показує, як обробляти ваші дані:

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 , це повільніше, ніж parseInt , але це зробить роботу. Я сумніваюся, що ви (або я) може зробити це краще, і є більш корисна річ, щоб зробити з нашим часом.

Редагувати:

Я профільований

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

з Arduino UNO і потрібно 147us для перетворення одного номіналу.

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];

 //Read 12 bytes (11 bytes of data + 1 byte delimiter)
  int nBytes = Serial.readBytes(packet, sizeof(CHANNELS + 1));

  if(nBytes == CHANNELS + 1) {
   //Convert bytes to ints;
    for(int i=0; i

Цей код не виявляє помилок. Для цього можна додати додатковий байт з контрольною сумою всього набору даних.

0
додано
Цікава проблема з цим, nBytes ніколи не буде 11 + 1, коли я перевірив його, він завжди буде 4, то 4, а потім 4, а не 12 за один раз. Будь-які ідеї?
додано Автор xandriksson, джерело
Ви робите майже правильно. При роботі з comms (Serial, tcp/ip) ніколи не припускайте, що ви отримаєте всі ваші байти в одному читанні. Ви повинні бути готові читати дані на шматки. У вас є основна ідея; тепер вам потрібно заповнити решту деталей. Іншою проблемою, що стоїть перед вами, є синхронізація обох комп'ютерів: потрібно зарезервувати спеціальне значення (наприклад, 255), щоб сигналізувати початок (або кінець) набору даних. Зараз, напевно, перші відправлені байти втрачені. Після того, як ви додасте всі ваші перевірки помилок, відновлення та буферизація вашого коду буде на 5 раз більше. Ось як це працює.
додано Автор user31481, джерело

Замість використання parseint, відправте дані для 11 смуг в дуже специфічній формі до Arduino, щоб Arduino може «розібрати» його дуже легко.

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

0
додано
Отже, якщо я надішлю "025" до arduino з послідовним, я можу використовувати serial.readBytes() ? І якщо я це зроблю, чи перетворить його на ціле число буде повільно, як з рядка?
додано Автор xandriksson, джерело
Вибачте моє незнання, але що саме ви маєте на увазі під байтом? Якщо кожна смуга має число між (і включаючи) 0 і 13, як я представляю це як байт?
додано Автор xandriksson, джерело
Ну, мені не потрібно посилати колір, мені просто потрібно знати, наскільки високою має бути кожна група. Отже, надсилання додаткових значень буде просто безглуздим.
додано Автор xandriksson, джерело
У цьому випадку достатньо 11 байтів, одне значення для кожної смуги.
додано Автор Pat, джерело
Байт 8 біт (8 нулів або один). З байтом (8 біт) можна зберігати число від 0 до 255 (або 256 різних значень). Ви навіть можете відправити 2 значення від 0-13 в один байт, поклавши шляхом обчислення value1 * 13 + value2 (який завжди буде менше 256). Або більше згідно з бітами: value1 << 4 + value2 ... Оператор << << переміщує біти чотири вліво, що рівно як множення на 16. правий (LSB) чотири біта. Тому, якщо ваша система змінюється на 16 каналів, вам не потрібно нічого змінювати (у порівнянні з 11 каналами, які використовують цей канал).
додано Автор Pat, джерело
Ну а краще не надсилати "025", тому що це 3 символи (байти). Наприклад, можна надіслати перші два на 0 * 16 + 2 = 2 ... так що відправте байт зі значенням 2 (це не символ 2).
додано Автор Pat, джерело
IT KPI C/С++ новым годом
IT KPI C/С++ новым годом
747 учасників

Чат обсуждения С/С++. - Вопросы "напишите за меня лабу" - это оффтоп. - Оффтоп, флуд, оскорбления и вбросы здесь не приняты. - За нарушение - предупреждение или mute на неделю. - За спам и рекламу - ban. Все чаты IT KPI: https://t.me/itkpi/1147