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

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

Перевод: Максим Кузьмин (Cubewriter) Контакты:</br>* Skype: cubewriter</br>* E-mail: cubewriter@gmail.com</br>* Максим Кузьмин на freelance.ru
Проверка/Оформление/Редактирование: Мякишев Е.А.


Интерпретация последовательно передаваемых данных [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);

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

См.также

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