Как построить шину CAN с помощью ESP32

Как построить шину CAN с помощью ESP32

Большинство людей ассоциируют семейство микроконтроллеров ESP с WiFi, что вполне логично, ведь они стали оптимальным решением для быстрого и простого подключения вашего проекта к интернету. Однако, хотя возможность WiFi может быть звездой шоу, ESP32 также оснащен CAN-контроллером. Просто мы не видим, чтобы он использовался так часто.

Умение читать и разбирать CAN-сообщения на вашем ESP32 может быть невероятно полезным. Например, вы можете получать данные с вашего автомобиля, такие как температура охлаждающей жидкости, положение дроссельной заслонки, скорость автомобиля и обороты двигателя, и отображать их на самостоятельно размещённой веб-странице, доступной по WiFi. Или вы можете создать свою собственную CAN-сеть.

В этом руководстве мы рассмотрим основы. Вы узнаете об основах системы шины CAN, как настроить встроенный CAN-контроллер ESP32, как подключить ESP32 к внешнему CAN-трансиверу TJA1050, и, что немаловажно, как заставить две платы ESP32 обмениваться данными по шине CAN.

Основы системы шины CAN

В старые времена в автомобилях было более 2000 метров кабеля, соединяющего, например, переключатели на приборной панели напрямую с фарами и задними фонарями. По мере усложнения автомобилей эта простая логика перестала работать.

В 1986 году компания Bosch представила систему шины CAN, которая решила эту проблему и сделала производство автомобилей дешевле и проще. CAN теперь является отраслевым стандартом и используется во всём: от легковых автомобилей и грузовиков до автобусов и тракторов, и даже в самолётах и кораблях.

Чтобы лучше понять шину CAN, представьте свой автомобиль как человеческое тело. В этом контексте шина CAN — это нервная система.

Аналогия шины CAN с нервной системой

Подобно тому, как нервная система обеспечивает связь между различными частями тела, шина CAN обеспечивает связь между различными узлами шины CAN, также известными как электронные блоки управления (или ЭБУ).

Современный автомобиль имеет более 70 ЭБУ, каждый из которых отвечает за выполнение определённой задачи. Хотя эти ЭБУ могут эффективно выполнять отдельную задачу, им необходимо обмениваться информацией друг с другом. Например, блок управления двигателем отправляет текущие обороты двигателя на приборную панель, где они отображаются на тахометре; аналогично, контроллер двери водителя отправляет сообщение контроллеру двери пассажира для приведения в действие стеклоподъёмника.

CAN-сеть ЭБУ автомобиля

ЭБУ подключены к шине в конфигурации мульти-мастер. Это означает, что каждый ЭБУ может взять на себя управление шиной и передавать информацию (например, данные датчиков) по ней. Переданные данные принимаются всеми остальными ЭБУ на шине CAN. Каждый ЭБУ может затем прочитать данные и решить, принять их или проигнорировать.

Топология шины CAN

Физическая связь осуществляется через жгут проводов шины CAN, состоящий из двух проводов: CAN low и CAN high. Оба провода плотно скручены вместе, чтобы электромагнитные помехи одинаково влияли на сигнал в обоих проводах, тем самым минимизируя ошибки.

Витая пара CAN

Дальние концы кабеля терминированы резисторами на 120 Ом. Поскольку шина CAN является высокоскоростной шиной данных, если шина не терминирована, сигнал отразится обратно и будет интерферировать со следующим сигналом данных, идущим по линии, что потенциально нарушит связь и приведёт к отказу шины.

Шина CAN

Сигнализация CAN

Для передачи данных по этим проводам изменяются уровни напряжения. Эти изменения уровней напряжения затем преобразуются в логические уровни, позволяя узлам сети обмениваться данными друг с другом.

Сигнализация CAN

Для передачи «логической 1» по шине CAN напряжение на обоих проводах устанавливается на 2,5 вольта (то есть разница напряжений отсутствует). Это состояние известно как рецессивное состояние и указывает на то, что шина CAN доступна для использования любым узлом.

Напротив, для передачи «логического 0» линия CAN high устанавливается на 3,5 вольта, а линия CAN low — на 1,5 вольта (то есть разница напряжений составляет 2 В). Это состояние шины известно как доминантное состояние, которое сообщает каждому узлу на шине, что другой узел передаёт данные и что следует подождать окончания передачи, прежде чем отправлять своё сообщение.

Узел шины CAN

Каждый узел CAN содержит CAN-трансивер, CAN-контроллер и микроконтроллер.

Узел CAN ЭБУ

CAN-трансивер

  • При приёме: преобразует уровни напряжения на шине CAN в уровни, понятные CAN-контроллеру.

  • При передаче: преобразует поток данных от CAN-контроллера в уровни шины CAN.

CAN-контроллер

  • При передаче: последовательно передаёт сообщение от микроконтроллера на шину, когда шина свободна.

  • При приёме: сохраняет принятые последовательные биты с шины до тех пор, пока не будет доступно полное сообщение, и инструктирует микроконтроллер забрать его (обычно путём генерации прерывания).

Микроконтроллер

Он решает, что означают полученные сообщения и какие сообщения он хочет передать. К нему подключены датчики, исполнительные механизмы и устройства управления.

Стандартный кадр CAN

Связь по шине CAN осуществляется посредством кадров CAN. Вот стандартный кадр CAN с 11-битным идентификатором. Давайте кратко рассмотрим каждое из восьми полей сообщения:

Кадр CAN
  • SOF: Start of Frame (начало кадра) — это «доминантный 0», сообщающий другим узлам, что узел CAN намеревается передавать.

  • ID: ID — это идентификатор кадра. Он используется для указания того, что означает сообщение и кто его отправляет. ID также определяет приоритет: чем ниже ID, тем выше приоритет сообщения.

  • RTR: Remote Transmission Request (запрос удалённой передачи) указывает, отправляет ли узел данные или запрашивает данные у другого узла.

  • Control: Поле управления содержит бит расширения идентификатора (IDE), который является «доминантным 0» для 11-битного формата. Оно также содержит 4-битный код длины данных (DLC), указывающий, сколько байтов данных будет в сообщении.

  • Data: 8 байтов данных содержат фактическую информацию.

  • CRC: Циклический контроль избыточности используется для обнаружения ошибок.

  • ACK: Слот ACK указывает, подтвердил ли узел приём и правильно ли получил данные.

  • EOF: EOF обозначает конец кадра CAN.

Примечание

Шина CAN уникальна тем, что это протокол, основанный на сообщениях. Обычно в распределённой сети каждое устройство имеет уникальный ID, отличающий его от других устройств на той же шине, и сообщения отправляются от устройства A к устройству B на основе их ID.

Напротив, узлы на шине CAN не имеют ID. Вместо этого каждому сообщению присваивается уникальный CAN ID, указывающий, о чём это сообщение. Все узлы принимают все сообщения, и каждый узел фильтрует сообщения, которые для него актуальны.

CAN-контроллер ESP32, он же контроллер TWAI

Espressif Systems называет свой CAN-совместимый контроллер TWAI, что расшифровывается как Two-Wire Automotive Interface (двухпроводной автомобильный интерфейс).

Вы можете задаться вопросом, почему они перешли с более привычного термина CAN на TWAI. Это распространённый вопрос, который задавали многие с тех пор, как Espressif решила переименовать его.

Ранее Espressif использовала термин CAN во всей своей документации для описания этой функции. Однако в своей последней документации они начали использовать термин TWAI. Это изменение может показаться немного запутанным поначалу, но имейте в виду, что TWAI и CAN по сути означают одно и то же, когда речь идёт о ESP32.

Контроллер TWAI разработан для совместимости с протоколом CAN 2.0. Он поддерживает как стандартный формат кадра (11-битный ID), так и расширенный формат кадра (29-битный ID).

Он включает функции обнаружения и обработки ошибок, обеспечивая надёжную связь в зашумлённых средах, что особенно важно в автомобильных приложениях.

Контроллер TWAI также поддерживает связь на основе прерываний, что позволяет эффективно обрабатывать сообщения и события на шине CAN без необходимости постоянного опроса состояния.

Блок-диаграмма

На диаграмме ниже показаны основные функциональные блоки контроллера TWAI.

Функциональная блок-диаграмма контроллера TWAI ESP32

Сигнальные линии

Интерфейс контроллера TWAI состоит из четырёх сигнальных линий: TX, RX, BUS-OFF и CLKOUT. Приятная особенность ESP32 заключается в том, что все четыре сигнала могут быть назначены на любой GPIO-пин.

  • TX и RX: Сигнальные линии TX и RX должны быть подключены к внешнему трансиверу. Обе сигнальные линии представляют/интерпретируют доминантный бит как низкий логический уровень (0 В), а рецессивный бит как высокий логический уровень (3,3 В).

  • BUS-OFF: Сигнальная линия BUS-OFF не всегда необходима, но полезна в определённых ситуациях. Эта линия устанавливается на низкий логический уровень (0 В), когда контроллер TWAI входит в состояние bus-off, указывая на нарушение связи. В нормальном режиме работы линия BUS-OFF поддерживает высокий логический уровень (3,3 В).

  • CLKOUT: Сигнальная линия CLKOUT — это ещё одна опциональная функция. Она обеспечивает вывод масштабированной версии тактового сигнала контроллера.

Режимы работы

TWAI поддерживает следующие режимы работы:

  • Нормальный режим: В этом режиме контроллер TWAI может участвовать в активности шины, такой как передача и приём сообщений/кадров ошибок. При передаче сообщения требуется подтверждение от другого узла.

  • Режим без подтверждения: Этот режим аналогичен нормальному, с одним ключевым отличием: передачи считаются успешными без необходимости подтверждения. Этот режим особенно полезен для самотестирования контроллера TWAI, так как он позволяет осуществлять обратную петлю передач без внешних подтверждений.

  • Режим только прослушивания: В этом режиме контроллер TWAI играет пассивную роль. Он не влияет на шину, то есть не будет передавать никаких сообщений, подтверждений или кадров ошибок. Контроллер по-прежнему может принимать сообщения, но не будет их подтверждать. Этот режим подходит для приложений мониторинга шины.

Особенности

Основные особенности контроллера TWAI ESP32:

  • Совместимость с протоколом ISO 11898-1 (CAN Specification 2.0)

  • Поддержка стандартного формата кадра (11-битный ID) и расширенного формата кадра (29-битный ID)

  • Скорости передачи данных:

    • от 25 Кбит/с до 1 Мбит/с в ревизии чипа v0.0/v1.0/v1.1

    • от 12,5 Кбит/с до 1 Мбит/с в ревизии чипа v3.0/v3.1

  • Множество режимов работы

    • Нормальный

    • Только прослушивание (без влияния на шину)

    • Самотестирование (передачи не требуют подтверждения)

  • 64-байтный приёмный FIFO

  • Специальные передачи

    • Одиночная передача (не выполняет автоматическую повторную передачу при ошибке)

    • Самоприём (контроллер TWAI одновременно передаёт и принимает сообщения)

  • Фильтр приёма (поддерживает режимы одиночного и двойного фильтра)

  • Обнаружение и обработка ошибок

    • Счётчики ошибок

    • Настраиваемый предел предупреждения об ошибках

    • Захват кода ошибки

    • Захват потери арбитража

Отличным ресурсом для изучения контроллера TWAI ESP32 является Техническое справочное руководство ESP32. Оно включает раздел, посвящённый TWAI, предоставляющий обширную информацию и рекомендации.

Техническое справочное руководство ESP32

Высокоскоростной CAN-трансивер TJA1050

Хотя ESP32 имеет встроенный CAN-совместимый контроллер, в нём нет встроенного CAN-трансивера, поэтому мы должны использовать внешний для подключения к CAN-сети.

Тип внешнего трансивера, который вам понадобится, зависит от конкретных требований вашего проекта. Например, если вам нужна совместимость со стандартами ISO 11898-2, то такие решения, как высокоскоростной CAN-трансивер TJA1050 от NXP или SN65HVD23x от Texas Instruments, будут отличным выбором.

В этом руководстве мы будем использовать модуль CAN-трансивера TJA1050.

CAN-трансивер TJA1050

Этот модуль служит интерфейсом между ESP32 и физической двухпроводной шиной CAN и соответствует автомобильным требованиям по высокой скорости (до 1 Мб/с), низкому току покоя, электромагнитной совместимости и электростатической разрядке.

Для получения дополнительной информации о CAN-трансивере TJA1050, пожалуйста, обратитесь к техническому описанию ниже.

Техническое описание TJA1050

Подключение оборудования

Теперь, когда мы знаем всё о контроллере TWAI, давайте построим нашу собственную CAN-сеть.

Пример 1: Простая двухузловая CAN-сеть

В этом примере строится простая двухузловая CAN-сеть — один узел передаёт сообщение, другой его принимает.

Для начала возьмите модуль CAN-трансивера TJA1050 и подключите его вывод VCC к VIN на вашем ESP32. Затем подключите вывод GND к земле. Далее подключите TX к GPIO5 и RX к GPIO4 на ESP32.

Вам нужно будет создать две таких схемы. Они будут идентичны по подключению. Одна будет функционировать как передатчик, отправляя сообщения, а другая будет служить приёмником, принимая эти сообщения.

Соединение модулей простое: CAN L подключается к CAN L, а CAN H подключается к CAN H. Провода в идеале должны быть витой парой, но для простого тестирования на макетной плате или других коротких соединений это не обязательно.

Важно помнить, что с увеличением длины шины или уровня электрических помех окружающей среды использование витой пары и добавление экранирования становится более важным.

Соберите сеть, как показано на рисунке.

Подключение двухузловой CAN-сети ESP32

Пример 2: Многоузловая CAN-сеть

В этом примере строится более крупная CAN-сеть — несколько узлов отправляют сообщения, а один узел ретранслирует их на ПК через последовательный порт.

Другие узлы могут быть добавлены между двумя конечными узлами. Они могут быть врезаны в линию или подключены к основной шине с помощью короткого ответвительного кабеля, при условии что его длина не превышает 30 см (12 дюймов).

Одно важное замечание о модуле CAN-трансивера TJA1050: он поставляется с уже впаянным терминирующим резистором на 120 Ом. Поэтому для дополнительных узлов в вашей сети вам нужно будет удалить этот резистор.

Соберите сеть, как показано на рисунке.

Подключение многоузловой CAN-сети ESP32

Установка библиотеки

Библиотека Arduino CAN от Sandeep Mistry — это действительно отличная библиотека для работы с контроллером TWAI. Вам нужно будет скачать и установить её в вашу Arduino IDE.

Для установки библиотеки перейдите в Sketch > Include Library > Manage Libraries… Дождитесь, пока менеджер библиотек загрузит индекс библиотек и обновит список установленных библиотек.

Управление библиотеками

Отфильтруйте поиск, введя «mcp2515». Найдите CAN от Sandeep Mistry. Нажмите на эту запись и выберите Install.

Установка библиотеки CAN Arduino

Пример кода

В этом простом тесте мы попытаемся передать сообщение «Hello World» по шине CAN, чтобы проверить, может ли оно быть декодировано. Это поможет вам научиться использовать модули и может послужить основой для более практических экспериментов и проектов.

Код для узла-передатчика

Загрузите этот скетч в узел-передатчик.

Примечание

Если у вас несколько узлов на шине CAN, загрузите этот скетч в каждый из узлов-передатчиков. Обязательно измените ID сообщений на уникальные значения для каждого узла.

#include <CAN.h>

#define TX_GPIO_NUM   5
#define RX_GPIO_NUM   4

void setup() {
  Serial.begin (115200);
  while (!Serial);
  delay (1000);

  Serial.println ("CAN Sender");

  // Set the pins
  CAN.setPins (RX_GPIO_NUM, TX_GPIO_NUM);

  // start the CAN bus at 500 kbps
  if (!CAN.begin(500E3)) {
    Serial.println("Starting CAN failed!");
    while (1);
  }
}

void loop() {
  // send packet: id is 11 bits, packet can contain up to 8 bytes of data
  Serial.print("Sending packet ... ");

  CAN.beginPacket(0x12);
  CAN.write('h');
  CAN.write('e');
  CAN.write('l');
  CAN.write('l');
  CAN.write('o');
  CAN.endPacket();

  Serial.println("done");

  delay(1000);

  // send extended packet: id is 29 bits, packet can contain up to 8 bytes of data
  Serial.print("Sending extended packet ... ");

  CAN.beginExtendedPacket(0xabcdef);
  CAN.write('w');
  CAN.write('o');
  CAN.write('r');
  CAN.write('l');
  CAN.write('d');
  CAN.endPacket();

  Serial.println("done");

  delay(1000);
}

Код для узла-приёмника

Загрузите этот скетч в узел-приёмник.

#include <CAN.h>

#define TX_GPIO_NUM   5
#define RX_GPIO_NUM   4

void setup() {
  Serial.begin (115200);
  while (!Serial);
  delay (1000);

  Serial.println ("CAN Receiver");

  // Set the pins
  CAN.setPins (RX_GPIO_NUM, TX_GPIO_NUM);

  // start the CAN bus at 500 kbps
  if (!CAN.begin(500E3)) {
    Serial.println("Starting CAN failed!");
    while (1);
  }
}

void loop() {
  // try to parse packet
  int packetSize = CAN.parsePacket();

  if (packetSize) {
    // received a packet
    Serial.print ("Received ");

    if (CAN.packetExtended()) {
      Serial.print ("extended ");
    }

    if (CAN.packetRtr()) {
      // Remote transmission request, packet contains no data
      Serial.print ("RTR ");
    }

    Serial.print ("packet with id 0x");
    Serial.print (CAN.packetId(), HEX);

    if (CAN.packetRtr()) {
      Serial.print (" and requested length ");
      Serial.println (CAN.packetDlc());
    } else {
      Serial.print (" and length ");
      Serial.println (packetSize);

      // only print packet data for non-RTR packets
      while (CAN.available()) {
        Serial.print ((char) CAN.read());
      }
      Serial.println();
    }

    Serial.println();
  }
}

Демонстрация

После загрузки скетча откройте монитор последовательного порта со скоростью 115200 бод. Узел-передатчик отправляет стандартный CAN-пакет и расширенный CAN-пакет каждую секунду.

Вывод узла-передатчика CAN ESP32

Узел-приёмник принимает его и передаёт на ПК через последовательный порт.

Вывод узла-приёмника CAN ESP32