Несогласованное время в цикле arduino

У меня есть этот код:


Вывод выглядит следующим образом:

output

Почему непериодически появляются очень большие значения?

Спасибо

, 👍-1

Обсуждение

Serial.println ожидает заполнения буфера TX. А ATmega32u4 обрабатывает порт USB в фоновом режиме., @Juraj

есть ли способ сделать это быстрее?, @Dominik Jašek

Пожалуйста, без кода в виде изображения. Скопируйте и вставьте код как текст и отформатируйте его с помощью кнопки {}, @chrisl


3 ответа


1

Существует несколько «задач» и прерываний, которые выполняются в фоновом режиме и влияют на частоту вызова функции loop().

Например, таймер, usart...

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

и кстати: максимум ~1.1мс неплохо для такого маленького контроллера. :-)

И в дополнение к вашему вопросу в ваших комментариях: я не думаю, что есть способ ускорить цикл. Пока вам нужны другие задачи, вы должны дать им некоторое время.

,

Спасибо Sealion! Кроме того, исходная проблема для этого вопроса заключалась в том, что я управляю шаговым двигателем, и мне нужно общаться с Raspberry Pi с помощью UART. Проблема в том, что когда я отправляю какие-то данные о двигателе в RPi, а двигатель уже вращается очень быстро, я часто теряю шаг из-за слишком большой задержки. У вас есть идеи, как это решить?, @Dominik Jašek

Я не знаю, как реализована библиотека шаговых двигателей arduino и почему вы теряете шаги, но в целом: Убедитесь, что вы не используете ожидания занятости. Особенно, когда вы общаетесь с Rapsberry (не ждите, пока персонажи станут доступны)., @theSealion

Хорошо, я все еще выясняю причину потери шагов и, кажется, нашел ее. Я думаю, что это не имеет ничего общего с Serial.print. Я хотел бы запустить степпер со скоростью 3000 шагов в секунду. Это означает один импульс каждые 333,3 микросекунды. И, очевидно, когда есть задержка ~ 1,1 мс, это слишком большая задержка, чтобы не потерять шаг. Кстати, я использую библиотеку AccelStepper. Есть ли возможность разобраться в этом, @Dominik Jašek

Я не понимаю, что "максимум ~ 1,1 мс - это неплохо", @Juraj


2

Как объяснил theSealion в своем ответе, это может быть связано с различные фоновые задачи, выполняемые микроконтроллером. Однако я лично считаю, что 1 мс выглядит чрезмерным. Я предполагаю, что главный виновник — стек USB, передающий все эти байты по проводу.

Чтобы проверить это, я написал следующую программу. Он измеряет гистограмму времени выполнения цикла, а затем распечатывает ее через Серийный порт. Поскольку данные распечатываются только после измерения сделано, последовательный порт не выполняет никакой работы, пока тайминги измерено.

const int HISTOGRAM_LENGTH = 512;

uint16_t histogram[HISTOGRAM_LENGTH];

void print_histogram() {
    Serial.println(F("t (us)  count"));
    Serial.println(F("-------------"));
    for (int i = 0; i < HISTOGRAM_LENGTH; i++) {
        if (histogram[i] == 0) continue;  // пропустить нули
        Serial.print(i * 4);
        Serial.print('\t');
        Serial.println(histogram[i]);
    }
    Serial.println(F("-------------"));
    Serial.flush();
}

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

void loop() {
    static uint32_t last_time;
    uint32_t now = micros();
    uint32_t dt = now - last_time;
    uint32_t bin = dt / 4;  // разрешение micros() равно 4 мкс
    if (bin >= HISTOGRAM_LENGTH)
        bin = HISTOGRAM_LENGTH - 1;
    if (++histogram[bin] == UINT16_MAX) {
        print_histogram();
        exit(0);
    }
    last_time = now;
}

Вот результаты при запуске на Arduino Uno:

t (us)  count
-------------
8       21888
12      65535
16      632
20      323
56      1
-------------

Это показывает, что большинство итераций цикла занимает 8–12 мкс. Эти итерации которые были прерваны, заняло 16–20 мкс. Выброс на 56 мкс составляет скорее всего время между запуском программы и самым первым повторение loop().

Здесь единственным источником прерывания, влияющим на измерения, является прерывание по таймеру. Во время измерения он выстрелил ровно 955 раз. период, который согласуется с количеством отсчетов, записанных в 16–20 мкс. Из этих данных видно, что прерывание таймера занимает примерно 8 мкс, что вполне разумно. Опять же, я бы не считайте задержку в 1 мс разумной.

,

0

Кроме того, изначальная проблема этого вопроса заключалась в том, что я управляю шаговым двигателем, и мне нужно общаться с Raspberry Pi с помощью УАРТ. Проблема в том, что когда я отправляю некоторые данные о двигателе к RPi и мотор уже крутится очень быстро, я часто теряю шаг

Ваш код шагового двигателя должен работать очень стабильно, чтобы поддерживать скорость 3000 шагов в секунду без дрожания или пропуска шагов. Он не должен ждать связи или любой другой задачи, но должен быть в состоянии вовремя выдать команду следующего шага. Некоторые возможные методы для достижения этой цели:

  • Не публикуйте сообщение целиком в Pi, а отправляйте только несколько символов за раз между этапами.
  • Общайтесь с Pi более короткими, четко закодированными сообщениями и позвольте Pi расширить их, если это необходимо.
  • Используйте максимальную скорость передачи данных Uno & Пи может надежно использовать.
  • Выдавайте пошаговые команды в подпрограмме прерывания, управляемой таймером, при этом все операции пошагового управления выполняются в фоновом режиме между пошаговыми командами.
  • Используйте более быстрый процессор.
,