Serial.write синхронен
У меня хороший опыт работы в мире реального (/критического) времени, но я новичок в Arduino. Я заметил, что Serial.write синхронен. Если размер буфера >64 (размер USART), он длится время физической передачи минус 64 символа. Это очевидно с помощью простых тестов, например: Serial.write (char64) возвращается через 1 миллисекунду, если буфер USART пуст, и возвращается со скоростью 64*10/бод, если буфер USART заполнен. Кто-нибудь написал асинхронный Serial.write? Если нет, то кто может помочь мне написать это? То есть есть документы по USART, прерываниям и скоро? По сути, смысл в том, чтобы заполнить буфер и получить прерывание, когда он освободится, чтобы заполнить его снова.
Спасибо, что связались со мной.
@Olivier, 👍0
Обсуждение2 ответа
Лучший ответ:
Пока буфер не заполнен, Serial.write
работает асинхронно. Вам просто нужно избегать заполнения буфера.
К счастью, существует функция Serial.availableForWrite()
, которая возвращает количество байтов, доступных в буфере. Вы можете использовать это, чтобы узнать, есть ли в буфере место для того, что вы хотите отправить, и если есть, то отправьте это (т. е. просто поместив это в буфер).
Переписывая обработку UART для вызова вашей собственной процедуры прерывания для отправки байта по мере необходимости, вы, по сути, делаете то же самое, что делает класс HardwareSerial в Arduino — только с вашим собственным буфером вместо буфера в Класс HardwareSerial.
В Arduino нет аппаратного буфера. Он имеет только однобайтовый сдвиговый регистр для отправки данных UART. Любая буферизация должна выполняться программно, поэтому либо вы реализуете ее самостоятельно, либо используете существующую буферизацию, либо генерируете и отправляете только один байт за раз, что совершенно бесполезно.
Если вы действительно хотите его переписать, вся необходимая информация находится в таблице данных MCU на выбранной вами плате разработки.
Термины «синхронный» и «асинхронный» применительно к подобным ситуациям лучше заменить терминами «блокирующий» и «неблокирующий», поскольку «синхронный» и «асинхронный» применительно к последовательному коммуникации подразумевают разные стили передачи данных, а не программную реализацию.
Спасибо за ответ, очень понятно и полезно. Исходя из этого, я воспользуюсь обоими вашими предложениями: 1 Сначала проверьте, прежде чем писать. 2 Поскольку размер буфера является программным, увеличьте его с 64 до 85, чтобы иметь возможность сразу отправить фразу NMEA0183, и ждите, только если следующая фраза появится в ближайшее время. Я согласен, что синхронный/асинхронный вариант можно неправильно понять из-за протокола передачи данных. Но вы понимаете!, @Olivier
Я проверил, HardwareSerial.h начинается с #if !define(SERIAL_TX_BUFFER_SIZE), то есть его можно определить снаружи раньше (именно то, что мне нужно). Но где мне разместить свой #define SERIAL_TX_BUFFER_SIZE xx? Это в самом Arduino Sketch или в каком-то файле .h? Если этот размер >256, используется специальный режим. Улучшает ли это производительность или нет? Я предполагаю, что каждый Serialx (на Mega) имеет свои частные буферы TX и RX. Все ли буферы TX имеют одинаковый размер? Или мне настроить каждый? Заранее спасибо., @Olivier
Я считаю, что они все одного размера. Вы хотите поместить определение в начало файла HardwareSerial.h, чтобы HardwareSerial.cpp и т. д. подхватили его., @Majenko
Благодаря Маженко у меня есть кусочки глобального решения. Я просто резюмирую и делюсь: Serial.availableForWrite() предоставляет свободное пространство в буфере Serial TX. Если ваша строка короче, Serial.write будет передаваться, если не блокироваться (пока не будет достаточно свободного места). Вы можете настроить размер этого буфера TX (по умолчанию — 64) в HardwareSerial.h. Будьте осторожны: размер буфера больше 256 (доступно 255) приводит к увеличению времени обработки Serial.write. Если у вас есть Serial, Serial1,... размер будет одинаковым для обоих, и, конечно, буферы будут разделены. Я заметил, что Serial.flush блокируется долго (80 мс). Это необходимо принять во внимание для критических по времени вопросов. Наслаждайтесь
Если вы измените размер буфера TX, обратите внимание, что он должен быть степенью двойки. В противном случае время обработки HardwareSerial::write() и TX ISR будет намного дольше. По сравнению с этим снижение производительности для размеров больше 256 невелико., @Edgar Bonet
Спасибо тебе за это, Эдгар. Я изучил несколько уровней кода C++ для Serial.xx, вплоть до HardwareSerial.xx. Я хотел реализовать очень простой и быстрый SerialTXClear для очистки очереди буфера TX, но чувствую, что пропущу слой. Где я могу найти документацию по этим многослойным материалам?, @Olivier
Мне не известен какой-либо программный уровень, кроме упомянутых вами файлов., @Edgar Bonet
- Как разделить входящую строку?
- Как вывести несколько переменных в строке?
- В чем разница между Serial.write и Serial.print? И когда они используются?
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Программы построения последовательных данных
- Как узнать частоту дискретизации?
- Что такое Serial.begin(9600)?
- Очистить существующий массив при получении новой последовательной команды
вы можете протестировать перед записью с помощьюavailableForWrite(), @Juraj