Все мои микроконтроллерные устройства, которые обмениваются данными с ПК через UART, используют строки ASCII для отправки команд и получения данных (как это реализовано в Arduino). Это то, что я понял, когда начал копаться в электронике, и мне всегда было достаточно посылать голые струны. Однако я заметил, что большинство устройств, с которыми я сталкивался, используют сложные бинарные протоколы, которые включают функциональные коды, адреса и проверку ошибок CRC.
Когда приемлем базовый обмен данными ASCII, а когда следует рассмотреть более продвинутые возможности, такие как Modbus? Используют ли коммерческие устройства такой код ASCII? Промышленный?
ASCII и CRC не исключают друг друга. ASCII — это кодировка, а CRC — для проверки ошибок.
ЧТО-НИБУДЬ может быть отправлено как ASCII. Мы, старики, наверняка помним UUEncoding, который превращает что угодно в строку ASCII.
A) Для меня это обычно вопрос скорости и эффективности. Отправка большого 32-битного числа с помощью ASCII может занять много времени, но для отправки его в двоичном виде по последовательному протоколу требуется всего 4 байта.
B) Отправка NUMBERS через ASCII означает, что вы должны преобразовать число в ASCII, что является явным дополнительным шагом (это часть того, что делает «printf»).
Если вы каким-то образом потеряете свое место, облажаетесь, потеряете формат, получите неправильный порядок байтов и т. д., бинарный протокол связи, безусловно, может облажаться. Если вы отправляете ASCII, может быть проще восстановиться после ошибок, просто войдя и ПОСМОТРЕВ на поток данных.
Вот некоторые мысли по этому поводу:
На простейшем уровне можно сказать, что простой протокол связи имеет три уровня: физический, транспортный и прикладной. (Есть модели с большим количеством, например, OSI с 7 или TCP/IP с 4. Количество уровней не очень важно в контексте этого вопроса.)
Прикладной уровень — это уровень, с которым вы имеете дело непосредственно в своем коде, и в центре внимания находится вопрос. Что касается транспортного уровня, байт, который вы передали ему в send_data, является просто двоичным шаблоном, но вы можете интерпретировать его в коде своего приложения как букву «А». Вычисление CRC или контрольной суммы будет одинаковым независимо от того, считаете ли вы байт «A», 0x41 или 0b01000001.
Транспортный уровень — это уровень пакетов, на котором у вас есть заголовки сообщений и проверка ошибок, будь то CRC или базовая контрольная сумма. В контексте прошивки у вас может быть такая функция, как send_data, где вы передаете ей байт для отправки. Внутри этой функции он помещается в пакет, который говорит: «Эй, это обычное сообщение, требует подтверждения, а контрольная сумма — 0x47, текущее время — X». Этот пакет отправляется через физический уровень на принимающий узел.
На физическом уровне определяются электроника и интерфейс: разъемы, уровни напряжения, синхронизация и т. д. Этот уровень может варьироваться от пары дорожек, передающих сигналы TTL для базового UART на печатной плате, до полностью изолированной дифференциальной пары, как в некоторых CAN реализации.
На принимающем узле пакет поступает на физический уровень, распаковывается на транспортном уровне, после чего ваш двоичный шаблон становится доступным для прикладного уровня. Прикладной уровень принимающего узла должен знать, следует ли интерпретировать этот шаблон как «A», 0x41 или 0b01000001, и что с ним делать.
В заключение, почти всегда допустимо отправлять символы ASCII, если это требуется приложению. Важно понять вашу схему связи и включить механизм проверки ошибок.
Еще не упомянутый момент заключается в том, что независимо от того, используется ли ASCII или двоичный протокол, отправка символа стирания перед каждым пакетом гарантирует, что даже если линейный шум или ошибки кадрирования появятся до начала пакета, все символы после стирания будут удалены. out будет правильно кадрирован при отсутствии дополнительных шумов. В противном случае, если кто-то отправляет пакеты непрерывно и не включает никаких символов, которые гарантированно добьются ресинхронизации, возможно, что один сбой может испортить все, что последует, до следующей паузы в передаче. Символ 0xFF удобен, потому что он гарантирует, что любой получатель сможет выполнить повторную синхронизацию со следующим символом.
(*) 0xFF — называется стиранием, потому что кто-то, кто набирает ошибочный символ при вводе данных на бумажную ленту, может нажать кнопку «сдвинуть ленту назад» и нажать стирание, чтобы заменить ошибочно набитый символ на 0xFF, что игнорироваться большинством получателей).
Одним из преимуществ отправки строк ASCII является то, что управляющие коды могут использоваться для обозначения начала/конца сообщения. например, STX (символ 2) и ETX (символ 3) могут сигнализировать о начале и окончании передачи. В качестве альтернативы вы можете добавить простой перевод строки, чтобы отметить конец передачи.
При отправке двоичных данных это усложняется, поскольку для управляющего кода нельзя зарезервировать конкретный битовый шаблон (без дополнительных издержек или сложностей), поскольку действительный байт данных может иметь тот же шаблон.
ASCII просто прекрасен, я использую его практически во всех проектах. Это значительно упрощает отладку для мониторинга порта, и это может стать проблемой только в том случае, если нужно отправить много данных.
Еще один бонус: я использую последовательные радиоустройства для передачи сообщений между Arduino, и я могу использовать последовательный монитор, подключенный к моему ноутбуку, и вводить сообщения, чтобы выполнять определенные действия. Отлично подходит для тестирования.
Кроме того, отправку вещей в виде двоичного файла не невозможно отладить, и в зависимости от ваших инструментов вы можете извлечь двоичный файл и преобразовать его во что-то удобочитаемое для человека. Или, если вы знаете, что ищете, вы можете визуально проверить поток данных и распознать значения, где они должны быть, и найти ошибку таким образом, хотя и не так просто. т. е. вы узнаете шаблоны байтов и узнаете ожидаемые значения
Вместо Modbus рассмотрите HDLC . Вы получаете обнаружение ошибок (что важно на зашумленных последовательных линиях). Синхронизация надежна, побег надежен.
Я использовал HDLC в сетях RS-485 без проблем, и PPP также использует его.
ASCII через UART является наиболее популярным отчасти потому, что:
Это удобочитаемо для человека при отладке (мне еще предстоит увидеть логический анализатор, который не декодирует ASCII).
Это очень легко реализовать, у вас есть таблица ASCII через быстрый Google, которая хорошо стандартизирована.
Он имеет встроенную синхронизацию со стартовыми/стоповыми битами.
Практически весь мир любителей использует ASCII вместо последовательного, поэтому любые новые методы должны будут иметь дело с этим, а это ни в коем случае не просто.
Затем вы попадаете в ситуацию, когда вы начинаете отправлять определенную кодировку, например, отправляете представление в памяти числа с плавающей запятой по сравнению с преобразованием числа с плавающей запятой в ASCII, отправляете это через последовательный порт, который может быть намного больше, чем 4 байта, а затем конвертируете это обратно к представлению в памяти на хосте. Вместо этого вы просто отправляете 4-байтовое представление каждый раз. Конечно, вы можете начать кодировку самостоятельно, но тогда вам нужно настроить начальные и конечные теги, порядок и т. д.
Вместо этого можно использовать такие вещи, как Protobuf . На самом деле это использовалось в проекте, над которым я работал, и это было чрезвычайно полезно, оно выводит сообщения переменной длины, обрабатывает порядок байтов для вас и имеет несколько других интересных функций. Он также не такой большой по размеру кода, и вы можете указать, что все будет статически размещаться при запуске. Однако вам придется ввести контрольную сумму самостоятельно, если вам это нужно.
Евгений Ш.
Тут
Гонки легкости на орбите