Arduino:Основы/Интерпретация последовательно передаваемых данных

Материал из Онлайн справочника
Перейти к: навигация, поиск

Перевод: Максим Кузьмин (Cubewriter)
Перевел 3377 статей для сайта.

Контакты:

Проверка/Оформление/Редактирование: Мякишев Е.А.


Интерпретация последовательно передаваемых данных [1]

Последовательно передаваемые данные – это данные, которые передаются байт за байтом от одного устройства к другому. И то, как оба устройства (компьютер или микроконтроллер) будут интерпретировать эти данные, когда будет начало сообщения, когда будет его конец, и что делать с этими данными в процессе передачи из точки А в точку Б – решать именно вам.

Но перед тем, как вы сможете передавать сообщения, оба устройства должны договориться о том, с какой скоростью они будут обмениваться данными, о количестве битов в байте и т.д. Как правило, неплохим считается стандарт с 8 битами, без бита четности и с одним стоповым битом. Для небольшого количества данных подойдет скорость в диапазоне от 2400 до 9600 бод.

Для отображения буквенно-цифровых символов (букв, чисел и знаков препинания) компьютеры используют числа, содержащиеся в байтах. Существует стандартная кодировка под названием ASCII (расшифровывается как American Standard Code for Information Interchange, т.е. «американский стандартный код для обмена информацией»), которая присваивает каждой букве и цифре значение от 0 до 255. Например, заглавной «А» соответствует значение «65». Эту таблицу можно найти во многих мануалах к компьютерам, а также онлайн – например, здесь.

ASCII – это очень распространенная кодировка (хотя и не единственная), поэтому очень многие девайсы используют ее в своем последовательном протоколе. Кроме того, как можно судить из названия, ASCII во многом основана на английском коммуникационном процессе. Она удобна, т.к. любой ASCII-символ можно представить одним байтом, но в то же время имеет и ограничения. Другие буквенно-цифровые системы, которые имеют больше символов, чем в латинице, в ASCII представить нельзя. ASCII можно заменить Unicode, но, к счастью, самая распространенная форма Unicode (т.е. UTF-8) совместима с ASCII. Если хотите почитать о Unicode более подробно, предлагаю взглянуть на этот информативный пост от Умера Мэнсора (Umer Mansoor).

Если вы передаете одно число, значение которого меньше 255, то его можно уместить в байт. Этот вид сообщений довольно прост. Вы можете отсылать один и тот же байт снова и снова, и у компьютера не возникнет никаких проблем, чтобы его получить. Но если отсылать больше (как это обычно и бывает), все становится чуть сложнее – компьютер должен знать, где у получаемого сообщения начало и конец. Разные устройства, выполняя те или иные действия, используют разные коды. К примеру, если устройство вроде кассетного магнитофона, проигрывателя CD-дисков и т.д. управляется при помощи последовательной передачи данных, в руководстве к ним всегда будет раздел, в котором описываются сообщения, которые он ожидает получить, а также скорость получения этих сообщений.

При работе с платой Arduino преобразование исходного значения байта в итоговый ASCII-символ осуществляется при помощи модификатора DEC. Например, так:

  1. Serial.print(myVar, DEC);

То есть, если значением myVar будет 134, функция вернет три байта, содержащие символы «1», «3» и «4».

Допустим, вы должным образом запрограммировали микроконтроллер, подключили его к компьютеру и хотите приступить к передаче каких-нибудь данных. Но для начала нужно протестировать, что именно отсылает микроконтроллер, и для этого подойдет самая простейшая программа. На Mac с этой задачей хорошо справляется ZTerm, а на PC – HyperTerminal. Задайте те же скорость передачи данных и другие настройки, что и на микроконтроллере, и подключайтесь. Если в настройках будет вариант Flow Control (т.е. «управление потоком передачи данных»), поставьте None. В итоге вы должны увидеть все отправленные вами ASCII-символы. Самая большая трудность в последовательной передаче данных – сделать так, чтобы в итоге у вас были все необходимые байты, и чтобы все они были расположены в правильном порядке. Если компьютер (или микроконтроллер) во время приема данных от последовательного порта занят чем-то еще или его буфер переполнен (т.е. если получаете больше данных, чем компьютер способен обработать), то некоторые данные вы рискуете потерять. Нет какого-то одного способа проверить, правильные ли данные вы получаете, но есть несколько фундаментальных вещей, которые стоит иметь в виду:

Сколько байтов я отправляю? А получаю столько же?

Например, если микроконтроллер отсылает три переменные от трех датчиков, компьютер должен получить все три.

Я получаю байты в правильном порядке?

Если отправитель раз за разом отсылает «ABC» (так, что в итоге получается «ABCABCABCABC»), может случиться так, что получатель начнет получать эти сообщения не с самого начала (и получит в итоге что-то вроде «BCABCABCABCA»). Это может стать проблемой, если, к примеру, «A» отвечает за правый переключатель, «B» – за центральный, а «С» – за левый. Чтобы избежать этого, имеет смысл в начале или конце вашей строки данных вписать константу, которая будет отличаться от других значений. Например, если за символ «A» отвечает число от 0 до 100, и за «В» отвечает число от 0 до 100, и также за «С» отвечает число от 0 до 100, то вначале каждой строки полезно будет вписать значение 101. В программе на BASIC это может выглядеть следующим образом:

PICBasic Pro:

  1.  A var byte
  2.  B var Byte
  3.  C var Byte
  4.  headerByte var byte
  5. headerbyte = 101
  6. main:
  7.           ' Здесь генерируем значения для A, B и C
  8.   serout2 portc.6, 16468, [headerByte, A, B, C]
  9. goto main

BX-24:

  1. Dim A as byte
  2. Dim B as Byte
  3. Dim C as Byte
  4. dim headerByte as byte
  5. headerbyte = 101
  6. ' Здесь пишем код, чтобы задать последовательный порт
  7. do
  8.         ' Здесь генерируем значения для A, B и C
  9. call putQueue(OutputBuffer, headerByte, 1)
  10. call putQueue(OutputBuffer, A, 1)
  11. call putQueue(OutputBuffer, B, 1)
  12. call putQueue(OutputBuffer, C, 1)
  13. loop

Wiring/Arduino:

  1. char A;
  2. char B;
  3. char C;
  4. char headerByte = 101;
  5. void setup() {
  6.   Serial.begin(9600);
  7. }
  8. void loop() {
  9.   // Здесь генерируем значения для A, B и C
  10.   Serial.print(A, BYTE);
  11.   Serial.print(B, BYTE);
  12.   Serial.print(C, BYTE);
  13.   Serial.print(headerByte, BYTE);
  14. }

Но эту задачу можно выполнить и по-другому – методом «вызова и ответа». То есть девайс-отправитель ждет запроса на отправку данных, затем дожидается и отправляет, а затем ждет нового запроса. Таким образом, девайс-получатель будет знать, что в его буфере одновременно будет находиться лишь одна строка данных. То есть вам нужно сделать так, чтобы последовательный порт девайса-получателя мог уместить то количество данных, которые девайс-отправитель отсылает в своей строке, а так же так, чтобы девайс-отправитель умел ждать и отправлял данные только после того, как получит байт-запрос. Если в качестве отправителя выступает микроконтроллер, его можно запрограммировать следующим образом (см. ниже). В этом примере получатель, будучи готовым к получению новых данных, шлет отправителю запрос – букву «А», за которую в кодировке ASCII отвечает число «65».

PicBasic Pro:

  1. main:
  2.     ' Ждем входящих данных:
  3.    serin2 portc.7, 16468, [inputVar]
  4.    ' Если мы получили от PC байт со значением «65» (ASCII-значение для «А»),
  5.     ' отсылаем данные:
  6.    if inputVar = 65 then
  7.      serout2 portc.6, 16468, [A, B, C]
  8.    endif
  9. goto main

BX-24:

  1. ' Выясняем, есть ли что-нибудь в очереди:
  2. gotaByte = statusQueue(inputBuffer)
  3. ' Если в буфере есть данные, берем первый байт:
  4. if (gotaByte = true) then
  5.     call getQueue(inputBuffer, inByte, 1)
  6.         if inByte = 65 then
  7.             ' Высылаем байты:
  8.            call putQueue(OutputBuffer, A, 1)
  9.            call putQueue(OutputBuffer, B, 1)
  10.            call putQueue(OutputBuffer, C, 1)
  11.        end if
  12. else
  13.    inByte = 0
  14. end If

Arduino:

  1. // Выясняем, есть ли что-нибудь в очереди:
  2. if (Serial.available() > 0) {
  3. // Если в буфере есть данные, берем первый байт:
  4. char inByte = Serial.read();
  5.         if (inByte == 'A') {
  6.             // Высылаем байты:
  7.             Serial.print(A, BYTE);
  8.             Serial.print(B, BYTE);
  9.             Serial.print(C, BYTE);
  10.         }
  11. }

Мои данные являются частью какой-то более крупной переменной?

Допустим, вы отсылаете от BX-24 целочисленную переменную. Длина этой переменной – два байта, поэтому компьютер при получении этой переменной как раз получит два этих байта. Чтобы снова преобразовать эти два байта в одно число, воспользуйтесь этой формулой:

  1. integerVar = (byte1Var * 256) + byte2Var

Если непонятно, представьте, что конвертируете шестнадцатеричное значение в десятичное. В данном же случае каждый байт – это число с целочисленным основанием 256. Чтобы убедиться в том, что формула верна, попробуйте отослать константу с числом больше 256.

Мои данные искажаются при передаче?

Последовательная передача данных – штука непростая в том смысле, что оба девайса нужно правильно заземлить, а провода для отправки и передачи данных должны быть правильно изолированы и подключены. Разного рода электрический шум может проникнуть в вашу систему и помешать передаче данных. Чтобы минимизировать время на устранение неполадок, лучше начать с отправки сообщения с константой. К примеру, если вы планируете отослать три байта с данными от датчиков, не стоит начинать с отправки значений, действительно полученных от датчиков. Начните с констант.

PicBasic Pro:

  1. outByte var byte
  2. outByte = 65
  3. serout2 portc.6, 16468, [outByte]
  4. outByte = 66
  5. serout2 portc.6, 16468, [outByte]
  6. outByte = 67
  7. serout2 portc.6, 16468, [outByte]

BX-24:

  1. dim outByte as byte
  2. outByte = 65
  3. call putQueue(OutputBuffer, outByte, 1)
  4. outByte = 66
  5. call putQueue(OutputBuffer, outByte, 1)
  6. outByte = 67
  7. call putQueue(OutputBuffer, outByte, 1)

Arduino:

  1. Serial.print(65, BYTE);
  2. Serial.print(66, BYTE);
  3. Serial.print(67, BYTE);

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

См.также

Внешние ссылки

  1. Interpreting Serial Data