Совсем недавно мы разбирали протокол CAN, и вот сегодня продолжаем двигаться по "автомобильным стандартам". На очереди LIN, который также свое основное применение нашел в автомобильной промышленности, да и, в общем-то, для этого и был изначально создан.
Чуть забегу вперед - этому протоколу будет посвящено целых три статьи. Сегодня будет исключительно теория, максимально подробно и наглядно. Во второй части мы будем работать с LIN на практике при помощи STM32 с использованием аппаратных средств микроконтроллера. А вот в третьей части мы с нуля напишем свой собственный драйвер для LIN на базе UART.
А пока к теории... Итак, протокол LIN был создан в конце 90-х годов (первая версия спецификации относится к 1999 году) группой известных компаний, в основном, автопроизводителей. Среди них:
- Audi
- Volkswagen
- BMW
- Volvo
- Motorola
В целом архитектура шины выглядит следующим образом:
В чем же смысл, спросите вы, ведь есть же CAN? Так вот, использование LIN не исключает использование CAN, а скорее дополняет. LIN является однопроводной шиной, более дешевой, чем CAN, и используется для связи менее критичных для безопасности и для работы автомобиля узлов между собой. То есть основная связь по-прежнему обеспечивается протоколом CAN, а менее важные блоки и датчики уже подключаются по LIN:
Низкая стоимость обеспечивается в том числе тем, что для реализации протокола обычно используется обычный UART микроконтроллера. Все остальное, необходимое для работы шины, реализуется исключительно в ПО. Но это с программной точки зрения. Физически же все-таки требуется использование дополнительной микросхемы трансивера. И самый популярный кандидат бесспорно - TJA1021:
Скорость передачи данных также вполне стандартная для UART'а: от 1 - до 20 кБод, длина линии может достигать 40 м. Давайте теперь перейдем к самому интересному, к структуре пакета.
Структура пакета протокола LIN.
Каждый пакет состоит из заголовка (header) и непосредственно данных (data):
Причем важной особенностью является то, что на шине присутствуют два типа устройств - LIN Master (ведущий) и LIN Slave (подчиненный). При этом инициировать передачу данных может только Master. То есть Slave-устройство не может само по себе выслать в сеть данные, оно должно ожидать запроса от ведущего и никак иначе.
Таким образом, именно Master отправляет в шину заголовки пакетов. В зависимости от определенного бита заголовка (это мы разберем чуть позже) подчиненные устройства понимают, что им требуется сделать:
- выслать данные, которые запрашивает ведущий
- или продолжать прием данных, в случае если, например, Master выполняет конфигурацию Slave
Как видите, иерархия очень строгая )
С организацией обмена данными разобрались, теперь можно углубиться непосредственно в структуру уже упомянутых частей LIN-фреймов. Заголовок пакета состоит из нескольких частей:
- Поле Break - это поле представляет из себя 13 нулевых битов подряд.
- Поле Sync - поле синхронизации. Этот байт имеет определенное значение - 0x55. Именно это число выбрано по той причине, что в двоичном виде оно представляет из себя чередующиеся нули и единицы - 0b01010101. При помощи этого поля устройства могут настроить свою скорость передачи данных.
- Поле PID - поле идентификатора. В нем зашифровано следующее:
Старт и стоп-биты здесь играют ту же роль, что и при передаче данных по UART, и используются для каждого из передаваемых по LIN байт.
Из битов ID0...ID5 складывается непосредственно значение идентификатора. А поскольку под это выделено только 6 битов, то значит диапазон значений идентификатора составляет от 0 до 0x3F (0b111111). При этом значения от 0x3C до 0x3F являются служебными. Кроме того, в значении идентификатора содержится информация о количестве передаваемых в Frame Data байт:
Идентификатор | Кол-во байт |
---|---|
0x00-0x1F | 2 |
0x20-0x2F | 4 |
0x30-0x3F | 8 |
Итак, тут у нас остаются еще два бита четности, для них формула выглядит следующим образом:
P0 = ID0 \oplus ID1 \oplus ID2 \oplus ID4
P1 = \medspace!(ID1 \oplus ID3 \oplus ID4 \oplus ID5)
И на этом все, заголовок пакета сформирован.
Поле данных в свою очередь состоит из непосредственно байт данных (от 1-го до 8-ми байт) и контрольной суммы (1 байт):
Для расчета контрольной суммы есть два варианта:
- Классическая контрольная сумма (версия LIN1.x) - сумма всех байт данных из поля Frame Data с переносом. После суммирования полученный байт инвертируется.
- Расширенная контрольная сумма (версия LIN2.x) - используется такой же алгоритм, только в суммировании участвует еще и байт PID. Сообщения с заголовками 0x3C и 0x3D должны использовать классическую контрольную сумму.
Давайте рассмотрим пример расчета классической контрольной суммы. Пусть байты данных равны: 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37. Суммируем:
0x30 + 0x31 + 0x32 + 0x33 + 0x34 + 0x35 + 0x36 + 0x37 = 0x19C
Вычитаем из полученного значения 0xFF:
0x19C - 0xFF = 0x9D
И инвертируем, в итоге получаем:
!(0x9D) = 0x62
Для выбранных нами байт данных контрольная сумма равна 0x62.
В практической статье по протоколу LIN мы обязательно посмотрим, как все это будет выглядеть на деле, а пока на этом заканчиваем, до скорой встречи 🤝
Спасибо
Отличная статья. То что надо. Спасибо
Спасибо!