Как работает Arduino в режиме реального времени (реагирует на датчик)?

Новичок в arduino, извините за вопрос, который может показаться тривиальным.

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

Я бы хотел:

  • У меня есть физическое колесо, которое я поворачиваю на один оборот каждые 300 мс. Это продолжительность цикла во времени.
  • Считайте маркер на этом колесе с помощью датчика, такого как Sick или Omron. У меня это уже есть. Датчик может быть PNP или NPN, обеспечивая закрытое или открытое соединение. Я могу использовать оптопару, если нужно.
  • После обнаружения сигнала Arduino должна отправить (последовательную) строку с использованием RS232 на ПК. Около 90 символов. Не нужно ждать ответа.

Мой вопрос: будет ли время, когда Arduino будет делать это относительно постоянно, то есть отправлять данные примерно в одну и ту же точку цикла. Пример: сигнал считывается на "12" на часах, данные отправлены около "5".

Если возможно, какой тип Arduino мне нужен?

Или мне нужно что-то более реальное время для такого проекта, как ПЛК?

, 👍2

Обсуждение

Вы запрашиваете две разные вещи: задержку вашей системы и джиттер, @chrisl

Вы говорите об ответе на сигнал частотой примерно 3,33 Гц, который вы можете контролировать с помощью простого прерывания, запускаемого фронтом, используя микроконтроллер, работающий на частоте, почти в шесть миллионов раз превышающей эту. Вы должны быть в полном порядке, даже учитывая несколько сотен инструкций по отправке последовательных данных., @Austin Hemmelgarn


2 ответа


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

7

один оборот каждые 300 мс.

300 мс — это очень много для Arduino, даже для AVR-устройства. один, как Uno.

Я провел небольшой эксперимент. Я сгенерировал (с помощью Arduino...) периодический сигнал, состоящий из отрицательного импульса шириной 96 мкс, повторяющегося каждые 300 мс. Я отправил этот сигнал на Arduino Uno со следующим код:

const uint8_t sensor_pin = 3;

// 90-байтовое сообщение.
const char message[] = "Korem ipsum dolor sit amet, consectetur "
        "adipiscing elit sed do eiusmod tempor incididunt\r\n";

void setup() {
    pinMode(sensor_pin, INPUT_PULLUP);
    Serial.begin(9600);
}

void loop() {
    if (digitalRead(sensor_pin) == LOW) {
        Serial.write(message);

        // Подождем, пока сигнал снова не поднимется.
        while (digitalRead(sensor_pin) == LOW)
            continue;
    }
}

Это не самый лучший способ обнаружения границ, но для этого простого тестить получится. Вы можете заметить, что я не использую прерывания для захват импульса: 96 мкс достаточно для захвата этим простой digitalRead(). Затем я захватил входные и выходные сигналы двухканальным карманным осциллографом. Вот снимок экрана:

Снимок экрана осциллографа

Голубой след вверху – это полученный импульс. Желтая дорожка внизу является выходом последовательного порта. Отрицательный импульс на этой трассе, почти совпадает с принятым импульсом и является стартовым битом. Затем сигнал повышается для передачи первого бита данных, который равен 1 (это почему я выбрал «К», а не «Л» в качестве первой буквы).

На этом снимке видно, что задержка Arduino составляет крошечный в этом масштабе: небольшая часть продолжительности начального бита. Чего вы не видите, так это дрожания, которое довольно велико в относительном сроки, но все же малы по сравнению с актуальными для вас временными масштабами. В короткий, задержка между задним фронтом на входе и падающим край стартового бита последовательного порта колеблется примерно между 10 и 20 мкс. Да, это микросекунды. Эта задержка примерно в 20 000 раз короче периода вращения. Если сигнал считывается в «12» на часах, стартовый бит срабатывает в 12 часов с 0 минут и 2 секунды.

Обратите внимание, что, с другой стороны, время, необходимое для отправки сообщения в 9600 бит/с — это 93,6 мс, значительная часть оборота период. И я не смотрел на поведение преобразователя USB-to-serial чип, а это еще одна банка червей...

,

Нет никаких причин, по которым нельзя увеличить скорость последовательного порта. Я всегда использую 115200 в качестве скорости передачи, и никогда не было никаких проблем., @PMF

@PMF: У ОП есть очень веская причина не увеличивать скорость последовательного порта: он хочет смоделировать промышленное устройство, которое говорит со скоростью 9600 бит / с., @Edgar Bonet

Обратите внимание, что сообщение из 90 символов, вероятно, превысит размер выходного буфера (чипа, используемого в) Arduino. Очевидно, есть способы обойти это (отправить несколько байтов, отложить, отправить еще несколько...), но здесь есть риск определенного переполнения (последнее сообщение еще не было записано, когда колесо доходит до точки срабатывания) и неопределенный обгон (прерывания все еще обслуживаются, когда колесо приходит в движение). Первый случай будет достаточно легко распознать, а второй - менее... и может быть достаточно, чтобы вызвать проблемы, если это используется, например, для определения угла опережения зажигания., @Mark Morgan Lloyd

@EdgarBonet Я не вижу ничего в вопросе, указывающем скорость фактического устройства, которое он хочет смоделировать. Но если бы сообщение было на самом деле слишком длинным для передачи в течение временного интервала, это было бы то же самое и для реальной системы., @PMF

@MarkMorganLloyd: выходной буфер рассматриваемого чипа имеет глубину один байт. Два байта, если считать сдвиговый регистр, содержащий оставшиеся биты передаваемого байта. И тем не менее, код, который я разместил, не имеет проблем с надежной отправкой всего сообщения: Serial.write() умнее, чем вы думаете. Риска переполнения нет, если интервал между входными импульсами превышает 93,6 мс, и они поступают только каждые 300 мс., @Edgar Bonet

@EdgarBonet Большое спасибо, но HardwareSerial.h точно демонстрирует, насколько он умен: 16 или 64 байта в зависимости от типа микросхемы / платы. Уверяю вас, что это *ВОЗМОЖНО* обойти., @Mark Morgan Lloyd

@MarkMorganLloyd: Я хочу сказать, что даже с этим 64-байтовым внутренним буфером Serial.write() надежно отправляет 90-байтовую строку, если ее об этом попросят., @Edgar Bonet

@EdgarBonet По моему опыту, не во всех случаях. Однако /моя/ точка зрения заключается в том, что если ISR все еще очищает буфер в то время, когда приходит следующий импульс оборота, это, вероятно, приведет к неопределенному джиттеру... если, конечно, он не подключен к прерыванию с более высоким приоритетом, что я подозреваю маловероятно (и это определенно не так в вашем опубликованном примере)., @Mark Morgan Lloyd

@MarkMorganLloyd: Re «_Не во всех случаях по моему опыту_»: если Serial.write() не отправляет надежно 90 байтов при запросе, у вас есть ошибка в вашем коде. Вероятно, тот, который портит память. Относительно «_если ISR все еще очищает буфер.._»: в этом случае ожидается новое сообщение, в то время как предыдущее не было отправлено полностью. Конечно, будет дополнительный джиттер! Обойти это невозможно. К счастью для OP, сообщения отправляются 93,6 мс и отправляются каждые 300 мс., @Edgar Bonet

@EdgarBonet Я не согласен. Однако я думаю, что использовал Serial.print() и т. д., когда обнаружил это, а не Serial.write() (и что я не могу вспомнить точные символы), и я хотел бы предупредить, что даже в Serial.write() случае, если он не может поместить данные в доступный буфер, он, скорее всего, приостановит основной цикл (с эффектом на любую жестко запрограммированную задержку), а не волшебным образом выделит больше места для хранения. Однако ваша точка зрения о том, что 90-байтовое сообщение занимает примерно 90 мс при скорости 9600 бит/с, конечно, хорошая., @Mark Morgan Lloyd

@MarkMorganLloyd: Вы правы насчет паузы. В этом случае для завершения Serial.write() требуется 26 мс. Я не упомянул об этом в ответе, потому что считаю, что ОП не будет беспокоиться об этом, поскольку его главная забота заключалась в том, будет ли задержка в сотни миллисекунд., @Edgar Bonet


2

TLDR: любой Arduino, вероятно, достаточно для ваших целей, если все сделано правильно.

Я предполагаю правильный двоичный сигнал ВЫСОКИЙ/НИЗКИЙ от датчика, ВЫСОКИЙ, когда он "видит" маркер, НИЗКИЙ, если это не так.

Что может пойти не так?

  1. Вы используете digitalRead(), чтобы получить состояние цифрового входа. В зависимости от ширины импульса вы можете не поймать высокое состояние. Вместо этого используйте прерывания.
  2. Отправка данных через последовательный порт занимает больше времени, чем оборот машины. 90 байт при 9600 бит/с нужно (90 байт * 10 бит/байт / (9600 кбит/с) = 93 мс. За 300 мс достаточно времени, чтобы отправить 90 байт. А 9600 бит/с довольно медленно: вы, вероятно, можете используйте 115 200 бит/с для коротких или экранированных проводов.
  3. "Приблизительно 90 символов": рассчитать все для максимального количества данных, которые в итоге будут отправлены.
  4. Получение данных на стороне Arduino занимает слишком много времени. Вы не указали, откуда взялись эти 90 байт. Запросить у 90 устройств по байту данных за 300 мс может быть невозможно.
  5. Получение данных на стороне ПК (или что-то еще) может быть задержано, так как они буферизуются. В частности, адаптеры USB-Serial не имеют гарантированного времени. Вычислять USB-устройства сложно.
  6. Потоки на стороне ПК: из-за планирования потоков ОС приложение, которое должно получать данные, может не получать процессорное время в течение нескольких миллисекунд, 16 мс (1/64 с) довольно часто. Выполнение математических расчетов невозможно (попробуйте запустить 8 потоков в реальном времени, которые делают что-то еще, и ваш компьютер практически зависнет)
  7. Обработка на стороне ПК: неизвестно
  8. Отображение: после обработки до появления сигнала на экране, что может занять еще 16 мс (при частоте обновления 60 Гц).

настало бы время, чтобы Arduino сделал это относительно постоянным

Относительно: да. Конечно, это зависит от реализации (код Arduino C++).

Если данные для отправки жестко закодированы в 90 символов, я ожидаю, что полный сигнал будет отправлен до отметки «4 часа» (94 мс / 300 мс * 12 часов ~ 3 часа) . Я вижу проблему больше на принимающей стороне (см. пункты 5., 6., 7. и 8.).

какой тип Arduino мне нужен?

Я не знаю их всех, но Uno определенно подойдет. Если у вас есть только один цифровой вход, возможно, подойдут даже меньшие. Просто начните с Uno или того, что у вас есть, и посмотрите, что получится.

Мне нужно что-то более реальное время для такого проекта, как ПЛК?

Я не вижу необходимости в ПЛК в вашем проекте.

,

Ваш ответ также полезен, но мне пришлось принять другой раз для предоставления точных измерений. Поскольку это будет просто *симуляция*, 90 символов будут просто фиксированным текстом и увеличивающимся счетчиком, поэтому не нужно задерживать ввод данных из любого места. Принимающая сторона вполне способна обрабатывать ввод. К сожалению, переключение на 115k невозможно из-за симуляции реальной машины., @MyICQ

@MyICQ: принять другой ответ можно. Я не очень доволен решением digitalRead() этого ответа - возможно, потому, что у меня было слишком много проблем с digitalRead(), работающим недостаточно надежно в моих проектах. В любом случае, если предложенный код примерно соответствует тому, что вам нужно, это хороший ответ., @Thomas Weller

Что может быть альтернативой digitalRead()? Я новичок в Arduino, и каким-то образом должно запускаться событие отправки последовательной строки?, @MyICQ

@MyICQ: обратите внимание, что я случайно вычислил кбит/с вместо бит/с, поэтому я ошибся в 1000 раз. Боже мой! Смотрите новый расчет. Все данные отправляются примерно в 3 часа., @Thomas Weller

@MyICQ: используйте digitalRead(), как предложил Эдгар. Позже вы можете попробовать https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/, @Thomas Weller

1. Я тоже не большой поклонник digitalRead(), но его преимущество в простоте и удобстве для новичков. Если бы материал был действительно критичен ко времени, я бы использовал прерывания. С осторожностью, потому что печать в Serial из контекста прерывания имеет свои проблемы. 2. 90 байт на 9600/8N1 должно занимать 93,75 мс (не забываем стартовый и стоповый биты). При округленной Arduino скорости «9600 бит/с» это занимает 93,6 мс., @Edgar Bonet