Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Delphi: Для новичков > Принять данные от машины по k-line |
Автор: mr_smit 12.10.2011, 08:28 | ||||
Здравствуйте. Давно я не программировал на Delphi. А тут задался целью сделать для своей машины (ВАЗ 11183) считыватель ошибок на микроконтроллере. Чтобы разобраться с протоколом, хочу для начала из своего приложения на ПК посмотреть/почитать данные. Диагностическая линия (k-line) представляет из себя "обычный ком порт" только с измененными логическими уровнями. 0 вольт - это логический 0, +12 вольт - это логическая единица. У ком порта 0 это минус 12 вольт. Собственно все переходники для машины и представляют собой просто согласователи этих самых уровней. Скачал бесплатную программу Diagnostic Tools. Подключился к машине. Всё работает. Всё показывает. Взял снифер ком порта, посмотрел что шлет программа. Скачал описание протокола обмена. Чуда не произошло, всё строго по пунктам ![]() Не буду сильно вдаваться в подробности протокола. Собственно что меня интересует: 1. Чтобы начать диагностику, нам надо отправить: Запрос startCommunication: 81 10 F1 81 03 Ответ: 83 F1 10 C1 6B 8F 3F В ответе C1 означяет что ЭБУ готов с нами работать. Поставил компонент AsyncFree104 для работы с com портом:
Как мне прочитать ответ? И как оттуда вытащить C1 ? 2. Сдернул клемму. Прочитал ошибки свои. Для определения значений данного параметра используется десятичная (BCD) кодировка. Запрос ошибок readDTCByStatus: 84 10 F1 18 00 00 00 9D Ответ: 88 F1 10 58 02 04 43 E0 14 26 E0 24 P0443 - всё верно, 2 ошибки появилось. P1426 Первые 3 байта нас не интересуют. Нам интересно: 58 - положительный ответ, 02 - число ошибок, дальше ошибки разделенные E0. Последняя цифра во всех запросах/ответах - контрольная сумма (2 младших разряда). Как вытащить ошибки из этого ответа? У компонента есть:
Как принимать в этот Buf ? Если длинна его меняется. И как разобрать данные? |
Автор: bems 12.10.2011, 08:34 |
1. В чем вопрос-то? Как вытащить четвертый байт из буффера? 2. Выделять с запасом |
Автор: mr_smit 12.10.2011, 08:56 | ||
Пока писал, сам сообразил. Вроде так:
Так? |
Автор: bems 12.10.2011, 09:04 |
зависит от того что такое ReadBuffer, но похоже, да |
Автор: mr_smit 12.10.2011, 09:05 | ||||
Да, забыл написать
Если так, то тогда ещё вопрос: BCD кодировка, как с ней работать? Как мне, например, в Memo отобразить ошибки? |
Автор: bems 12.10.2011, 09:05 |
ну и там же наверное чтонить и возвращается из WriteData и ReadData |
Автор: mr_smit 12.10.2011, 09:32 | ||
Хочу прочитать ошибки из тестового массива.
В мемо получается: P467 P2038 А должно: P0443 P1426 Как перевести из BCD формата в String? |
Автор: AndreyIQ 12.10.2011, 09:49 | ||
|
Автор: mr_smit 12.10.2011, 10:15 | ||||
Оооооо....!!!! Спасибо! Работает. Кстати. Вот запрос параметров: 82 10 F1 21 01 A5 ЭБУ моей машины отвечает: 80 F1 10 26 61 01 3B 90 41 04 00 00 00 00 47 80 00 00 00 52 52 80 18 00 8E 00 5C 00 00 00 00 00 00 00 00 00 FF FF DD A4 47 02 CE 61 - Положительный ответ readDataByLocalIdentifier 01 - afterSalesServiceRecordLocalIdentifier (что то там....) .... 11-й байт - Температура охлаждающей жидкости .... Формула для расчета: N=E-40 [°C] E - передаваемое значение N - физическая величина У меня это значение 47. Берем windows калькулятор. Переводим 47 hex в dec. Получаем 71. Дальше 71-40=31°C. Соглашусь. На момент снятия данных машина часа полтора простояла на улице. До конца не остыла. Как бы мне теперь на Delphi так конвертнуть? |
Автор: AndreyIQ 12.10.2011, 10:26 | ||||
|
Автор: mr_smit 12.10.2011, 11:48 | ||
Чего то у меня в ошибку вываливается на строке:
Может где то длинну массиву назначать? |
Автор: AndreyIQ 12.10.2011, 11:58 | ||
SetLength. |
Автор: mr_smit 12.10.2011, 11:58 | ||||
Кажется понял, ReadBuffer у меня сейчас пустой, а я пытаюсь 4-й элемент массива прочитать. А где её задавать? Сразу после чтения?
Добавлено @ 12:07
Так? И нужно ли буфер обнулять перед каждым вызовом? Или он автоматически каждый раз с нулевого значения записывается? |
Автор: mr_smit 12.10.2011, 15:34 |
Чего то у меня не работает код ![]() Дело в том что, как я понимаю, поскольку по K-Line обмен идет по одному проводу, то в момент передачи, принимать ничего не надо! Закончили передавать - начинаем линию слушать. Вот как этот момент реализовать? Добавлено @ 15:39 Я когда сниффером обмен смотрю: Запрос startCommunication: 81 10 F1 81 03 Ответ: 81 10 F1 81 03 83 F1 10 C1 6B 8F 3F Реальный ответ это только 83 F1 10 C1 6B 8F 3F. А в начале идет запрос, просто он автоматом попадает на вход. Провод то связи один. Он и на передачу работает и на прием. |
Автор: northener 12.10.2011, 16:30 | ||
Слушать постоянно. Отделять ответ от своего же "эха" потом. |
Автор: northener 12.10.2011, 23:48 |
Какой именно код не работает? 1. Вы смогли самостоятельно разработать или где-то купить преобразователь СОМ-"однопроводная линия"? Это не так сложно теоретически, но я сомневаюсь, что вы его хоть как-то получили. А без него вы ничего не сможете сделать. 2. Вы изучили полностью тот компонент, который вы решили использовать? Наверняка нет. Все эти компоненты созданы по одному лекалу. И у них, у всех есть событие типа OnRxChar. Вот в нём, в этом событии и надо вызывать функцию ReadData. Кстати, заметьте. Это наверняка именно функция, а не процедура! 3. Умение пользоваться "сниффером" и т.п. очень полезное умение. Но оно мало помогает при недостаточном уровне знаний. |
Автор: mr_smit 13.10.2011, 07:44 | ||||
Я сам спаял преобразователь USB <-> K-Line. Там ничего сложного. Он работает! Я несколько программ диагностики уже использовал. Все видят контроллер. Все параметры в режиме реального времени. С этим нет проблем. http://radikal.ru/F/i060.radikal.ru/1109/48/d2378ce3b7d4.jpg.html
Да, я уже тоже про это подумал. Просто с наскока разобраться не получилось. Буду копать глубже теперь. Сниффер классная вещь когда под рукой описание протокола. Наглядно всё видишь.
Теперь я понимаю что это точно работать не будет. Надо принимать только после окончания передачи. P.S. ReadData - это процедура, а не функция. Справку на компонент прилагаю. |
Автор: mr_smit 13.10.2011, 08:09 | ||||||||
Стало быть как только выходной буффер освободился, т.е. все данные переданы - очищаем входной буффер, и читаем из него данные по событию DataRecived:
Правильно? |
Автор: mr_smit 13.10.2011, 09:05 | ||
Итак, нам надо отправить запрос startCommunication. При положительном ответе на него отправить startDiagnosticSession. При положительном ответе на него периодически отправлять testerPresent чтобы ЭБУ видел что тестер до сих пор подключен. Попробовал пока вот так это изобразить:
Что скажете? |
Автор: northener 13.10.2011, 11:25 | ||||
Теоретически правильно. Но на практике почти всегда неправильно. Ибо большинство таких компонент ххСОМPort игнорируют аппаратный FIFO СОМ-порта. А событие "Выходной буфер пуст" для них означает только то, что пуст выходной буфер драйвера СОМ-порта. Т.о. данное событие будет обработано гораздо раньше, чем СОМ-порт выдаст на линию всю предназначенную железяке посылку. Советую вам очищать входной буфер непосредственно перед посылкой команды железяке. И постоянно читать в событии
учитывая параметр Count. Принятое "эхо" вы сможете легко вычленить из приемного буфера. Ведь вы знаете, что вы посылали железяке. |
Автор: mr_smit 13.10.2011, 11:47 | ||||
А как???
Если так, то постоянно перезаписываться будет [0] элемент. Как ReadBuffer получить заполненный моим запросом + ответ? Я чего то не пойму никак. |
Автор: northener 13.10.2011, 12:56 | ||
Т.е. заведите переменную CurrentPos. Перед посылкой присваивайте ей 0. А затем
|
Автор: mr_smit 13.10.2011, 15:47 | ||
showmessage мне возвращает ноль ![]() Обмен смотрю сниффером. ЭБУ отвечает. Но ответ распознать не могу ![]() P.S. northener, сейчас попробую как вы говорите |
Автор: northener 14.10.2011, 00:20 |
Можете обращаться ко мне в личку. |
Автор: mr_smit 14.10.2011, 09:01 | ||||||||||
Из справки:
Перевод: Читает указанное число байт из входного буфера. Если не достаточно данных для чтения, вызывается исключение
Количество байт во входном буфере Получается вот так что ли?
|
Автор: northener 14.10.2011, 11:37 |
Может быть так. Может быть нет. Надо пробовать и смотреть что получается. Поймите меня правильно. Я не знаком с именно этим компонентом xxCOMport. Как правило "ReadData" в подобных компонентах - функция возвращающая количество байт реально вычитанных из приемного буфера. |
Автор: mr_smit 14.10.2011, 13:43 |
Я взял этот компонент потому что у него можно задать произвольную скорость обмена. Мне нужна 10400. Может подскажите знакомый вам компонент. Может что попроще. |
Автор: northener 14.10.2011, 21:44 | ||
Вообще-то произвольную скорость должен уметь ставить любой компонент. Особенно если он с исходниками. Посоветовать ничего не могу, потому что никаким никогда не пользовался. Только смотрел. Кроме библиотеки AsyncPro, но это слишком тяжелая вещь. |
Автор: northener 15.10.2011, 00:02 | ||
Ещё раз.
Т.е. 1. Создаём таймер. Его период выбираем исходя из скорости СОМ-порта, на которой работает железяка и из максимального количества байт посылки/команды и ответа. (Один байт - это как минимум 10 бит, как максимум 12 бит. Зависит от наличия/отсутствия бита чётности и от длины стоп-бита(1,1.5,2). Плюс запас 50-500 ms из-за того, что Винда не есть система реального времени. 2. Перед посылкой запроса/команды к железяке очищаем приемный/передающий буфер СОМ-порта методами компонента. 3. Сбрасываем в ноль переменную CurrentPos. 4. Посылаем запрос/команду железяке. Запускаем таймер. Читаем в событии OnDataRecived всё в буфер ReadBuffer по адресу ReadBuffer[CurrentPos]. 5. По срабатыванию таймера смотрим, чему равен CurrentPos и что мы уже получили. После анализа принятого возвращаться к пункту 2. 5-бис Смотреть "чему равен CurrentPos и что мы уже получили" можно уже в событии OnDataRecived после выполнения процедуры ReadData. И если всё уже принято, то сбрасывать/дизейблить таймер. И после анализа принятого переходить к пункту 2. |
Автор: mr_smit 18.10.2011, 17:03 | ||
Вобщем вот так всё заработало:
http://radikal.ru/F/s010.radikal.ru/i314/1110/bf/f821caa1623c.jpg.html northener, спасибо |
Автор: northener 18.10.2011, 23:53 |
Не за что. |
Автор: mr_smit 19.10.2011, 09:49 | ||
Единственное я не пойму почему запрос ошибок у меня не работает.
Сделал вывод массива на форму. Почему то запрос/ответ на чтение ошибок не проходит. Т.е. после нажатия на кнопку "ошибки" массив не обновляется. И в нем хранятся данные ещё с предыдущего запроса параметров. Странно. Вроде делаю CurrentPos:=0; http://radikal.ru/F/s017.radikal.ru/i431/1110/d9/36d27f8868bb.jpg.html |
Автор: northener 19.10.2011, 10:45 | ||
В приведенном куске кода есть обнуление CurrentPos. Есть посылка команды WriteData. А где чтение ответа? |
Автор: mr_smit 19.10.2011, 20:36 | ||
|
Автор: northener 19.10.2011, 23:29 | ||||||
Так. Я тогда не понимаю, чему вы радовались в ? И за что вы мне плюсик дали? Смотрим ваш кусок кода:
Добавлено через 13 минут и 27 секунд То ли лыжи не едут, то ли я ... Привожу полностью тот мой комментарий к коду, который начинался словами "А тут, в цикле, вы выводите из вашего приемного..." А тут, в цикле, уже выводите данные из вашего приемного буфера в Мемо! И что вы надеетесь увидеть в данный момент в вашем приёмном буфере? Да. После вызова метода компонента WriteData, компонент запустил свой механизм по передаче драйверу СОМ-порта данных для выдачи на линию. Плюс запустил в отдельном потоке функцию WaitCommEvent для получения события о том, что драйвер СОМ-порта что-то прочитал. И возможно даже драйвер уже успел что-то прочитать и сообщить об этом компоненту. Но ваша программа не сможет НИЧЕГО об этом узнать до выхода из той процедуры, кусок которой вы привели! Ибо пока вы не вышли из этой процедуры вашей программы, ваша программа не сможет обработать событие компонента ReadData. |
Автор: mr_smit 20.10.2011, 01:02 | ||
Плюс поставил за кусок кода:
В принципе я думал что просто не успевает массив заполнится данными. Но в режиме реального времени параметры у меня отображаются. Таймер настроен на 330 мс. Т.е. 3 раза в секунду считываю параметры. А как тогда тут поступить со считыванием ошибок по нажатию кнопки? Добавить как то задержку? Или 2 раза запрашивать? Почему когда я считываю данные - массив успевает обновляться, а когда ошибки считываю таким же образом, то массив не обновляется. |
Автор: northener 20.10.2011, 22:36 | ||
Приведите код вашей процедуры OnTimer. Я подскажу как её изменить. Тогда хоть я смогу (если смогу), хоть как-то отработать тот плюсик, который вы мне добавили. :( |
Автор: mr_smit 21.10.2011, 08:05 |
Вот: |
Автор: Korod 24.10.2011, 11:51 |
На СОМ-портах персоналки -12в - это лог.1, а +12в - это лог.0. |
Автор: northener 24.10.2011, 23:32 |
Чтобы не захламлять форум, ответил в личку. |
Автор: Darkblue 20.1.2012, 18:33 |
Уважаемые подскажите пожалуйста procedure TForm1.AfComPort1DataRecived ставится на рабочее поле,или просто прописывается в процедурах. Никак не могу научиться байт ловить ,помогите пожалуйста |
Автор: bems 20.1.2012, 20:39 | ||
|