Создание собственной CAN-сети с модулями MCP2515 и Arduino
В старые времена в автомобилях было более 2000 метров кабелей, напрямую соединяющих переключатели на приборной панели с фарами и задними фонарями, например. По мере усложнения автомобилей такая простая логика перестала работать. В 1986 году компания Bosch представила систему шины CAN, которая решила эту проблему и сделала производство автомобилей дешевле и проще. CAN теперь является промышленным стандартом и используется во всём — от легковых автомобилей и грузовиков до автобусов и тракторов, и даже самолётов и кораблей.
Умение читать и разбирать CAN-сообщения на Arduino позволит вам получать данные от вашего автомобиля, такие как температура охлаждающей жидкости, положение дроссельной заслонки, скорость автомобиля и обороты двигателя, и использовать их в своих проектах для приборной панели.
Модуль интерфейса CAN-шины MCP2515 — лучшее решение для добавления CAN-связи к Arduino через интерфейс SPI. Это руководство покажет, как подключить модуль MCP2515 к Arduino, но сначала — краткое введение в протокол CAN.
Основы системы CAN-шины
Контроллерная сеть (CAN-шина) — это стандарт связи, предназначенный для обеспечения коммуникации между внутренними устройствами транспортного средства без центрального компьютера.
Для лучшего понимания CAN-шины представьте свой автомобиль как человеческое тело. В этом контексте CAN-шина — это нервная система.
Подобно тому, как нервная система обеспечивает связь между различными частями тела, CAN-шина позволяет обмениваться данными между различными узлами CAN-шины, также известными как электронные блоки управления (ЭБУ, или ECU).
Современный автомобиль имеет более 70 ЭБУ, каждый из которых отвечает за выполнение определённой задачи. Хотя эти ЭБУ могут эффективно выполнять свою задачу, им необходимо обмениваться информацией друг с другом. Например, блок управления двигателем передаёт текущие обороты двигателя на комбинацию приборов, где они отображаются на тахометре; аналогично, контроллер двери водителя отправляет сообщение контроллеру двери пассажира для управления стеклоподъёмником.
ЭБУ подключены к шине в конфигурации мульти-ведущее устройство. Это означает, что каждый ЭБУ может взять управление шиной и передавать информацию (например, данные датчиков). Переданные данные принимаются всеми остальными ЭБУ на CAN-шине. Каждый ЭБУ может затем прочитать данные и решить, принять их или проигнорировать.
Топология CAN-шины
Физическая связь осуществляется через жгут проводов CAN-шины, состоящий из двух проводов: CAN low и CAN high. Оба провода плотно скручены вместе, чтобы электромагнитные помехи одинаково воздействовали на сигнал в обоих проводах, тем самым минимизируя ошибки.
Дальние концы кабеля терминированы резисторами 120 Ом. Поскольку CAN-шина является высокоскоростной шиной данных, если шина не терминирована, сигнал будет отражаться и создавать помехи следующему сигналу данных, потенциально нарушая связь и вызывая сбой шины.
Сигнализация CAN
Для передачи данных по этим проводам изменяются уровни напряжения. Эти изменения уровней напряжения затем преобразуются в логические уровни, позволяя узлам сети обмениваться данными.
Для передачи «логической 1» на CAN-шине напряжение на обоих проводах устанавливается на 2,5 вольта (т.е. разность напряжений отсутствует). Это состояние называется рецессивным состоянием и указывает, что CAN-шина доступна для использования любым узлом.
Напротив, для передачи «логического 0» линия CAN high устанавливается на 3,5 вольта, а CAN low — на 1,5 вольта (т.е. разность напряжений составляет 2V). Это состояние шины называется доминантным состоянием, которое сообщает каждому узлу на шине, что другой узел передаёт и следует подождать до завершения передачи.
Узел CAN-шины
Каждый узел CAN содержит CAN-трансивер, CAN-контроллер и микроконтроллер.
CAN-трансивер
При приёме: преобразует уровни напряжения на CAN-шине в уровни, понятные CAN-контроллеру.
При передаче: преобразует поток данных от CAN-контроллера в уровни CAN-шины.
CAN-контроллер
При передаче: последовательно передаёт сообщение от микроконтроллера на шину, когда шина свободна.
При приёме: сохраняет принятые последовательные биты с шины, пока не будет доступно полное сообщение, и указывает микроконтроллеру получить его (обычно вызывая прерывание).
Микроконтроллер
Решает, что означают полученные сообщения и какие сообщения необходимо передать. К нему подключены датчики, исполнительные механизмы и устройства управления.
Стандартный CAN-кадр
Связь по CAN-шине осуществляется через CAN-кадры. Вот стандартный CAN-кадр с 11-битным идентификатором. Давайте кратко рассмотрим каждое из восьми полей сообщения:
SOF: Start of Frame — «доминантный 0», сообщающий другим узлам, что CAN-узел намерен начать передачу.
ID: Идентификатор кадра. Определяет, что означает сообщение и кто его отправляет. ID также определяет приоритет: чем ниже ID, тем выше приоритет сообщения.
RTR: Remote Transmission Request — указывает, отправляет ли узел данные или запрашивает данные у другого узла.
Control: Содержит бит расширения идентификатора (IDE) — «доминантный 0» для 11-битного формата. Также содержит 4-битный код длины данных (DLC), указывающий количество байт данных в сообщении.
Data: 8 байт данных, содержащих фактическую информацию.
CRC: Циклический избыточный код для обнаружения ошибок.
ACK: Слот подтверждения — указывает, принял ли узел данные корректно.
EOF: End of Frame — отмечает конец CAN-кадра.
CAN-шина уникальна тем, что является протоколом на основе сообщений. Обычно в распределённой сети каждое устройство имеет уникальный ID для его отличия от других устройств на шине, и сообщения отправляются от устройства A к устройству B на основе их ID.
Напротив, узлы CAN-шины не имеют ID. Вместо этого каждому сообщению присваивается уникальный CAN-идентификатор, указывающий, о чём сообщение. Все узлы получают все сообщения, и каждый узел фильтрует сообщения, которые ему важны.
Обзор оборудования модуля MCP2515
Модуль интерфейса CAN-шины MCP2515 — это комплексное CAN-решение, включающее CAN-контроллер MCP2515 от Microchip и высокоскоростной CAN-трансивер TJA1050 от Philips. Это лучшее решение для добавления CAN-связи к Arduino через интерфейс SPI.
Благодаря надёжности и устойчивости модуль MCP2515 может использоваться в различных проектах, требующих надёжной передачи данных в шумных средах или на значительных расстояниях.
CAN-контроллер MCP2515
MCP2515 — это автономный CAN-контроллер, реализующий спецификацию CAN версии 2.0B. Он способен передавать и принимать как стандартные, так и расширенные кадры данных и удалённые кадры. MCP2515 включает маски и фильтры для отсеивания нежелательных сообщений, что снижает нагрузку на процессор.
MCP2515 имеет выходной вывод прерывания (INT), который может вызвать прерывание микроконтроллера при получении и загрузке действительного сообщения в один из приёмных буферов.
CAN-трансивер TJA1050
TJA1050 служит интерфейсом между CAN-контроллером MCP2515 и физической двухпроводной CAN-шиной, и соответствует автомобильным требованиям по высокой скорости (до 1 Мб/с), низкому потреблению тока в режиме покоя, электромагнитной совместимости и устойчивости к электростатическому разряду.
CAN-трансивер TJA1050 позволяет подключить к шине максимум 110 узлов.
Разъём CAN-шины
На плате имеется 2-полюсная винтовая клемма (обозначенная H и L) для подключения витой пары CAN-шины.
Модуль MCP2515 поддерживает скорости передачи данных до 1 Мб/с. Однако максимальная скорость передачи определяется длиной линии шины: более длинные линии — более низкая скорость. Максимальная длина шины при 1 Мб/с составляет 40 метров, при 125 кб/с — до 500 метров.
Терминация узлов
CAN-шина должна быть терминирована на обоих концах резисторами для предотвращения отражений сигнала. Поэтому модуль MCP2515 оснащён терминирующим резистором 120 Ом и перемычкой терминации. Для включения резистора перемычка должна быть установлена.
Если модуль является первым или последним узлом CAN-сети, перемычку следует установить. Если модуль — промежуточный узел, перемычку следует снять.
Технические характеристики
Вот технические характеристики:
Рабочее напряжение |
4.75 to 5.25V (на основе требований TJA1050) |
|---|---|
Спецификация CAN |
Version 2.0B at 1 Mb/s |
Частота кристалла |
8Mhz |
Буферы передачи |
Три буфера передачи с приоритизацией и функциями отмены |
Буферы приёма |
Два буфера приёма с приоритизированным хранением сообщений |
Фильтры сообщений |
Шесть 29-битных фильтров |
Маски |
Две 29-битные маски |
Выходы прерываний |
Одно прерывание с настраиваемыми разрешениями |
Интерфейс |
High-Speed SPI (10 MHz) |
Для дополнительной информации о CAN-контроллере MCP2515 обратитесь к техническому описанию ниже.
Для дополнительной информации о CAN-трансивере TJA1050 обратитесь к техническому описанию ниже.
Распиновка модуля MCP2515
Теперь давайте рассмотрим распиновку.
Входной разъём
Выводы на одной стороне модуля используются для связи с микроконтроллером.
INT — генерирует прерывание при получении и загрузке действительного сообщения в один из приёмных буферов.
SCK — вывод тактирования SPI.
SI — вывод последовательных входных данных / MOSI, для данных, отправляемых от Arduino к модулю.
SO — вывод последовательных выходных данных / MISO, для данных, отправляемых от модуля к Arduino.
CS — вывод выбора кристалла. Должен быть установлен в LOW для начала SPI-транзакции.
GND — общая земля для питания и логики.
VCC — это вывод питания. Подключайте только к источнику питания 5V.
Разъём CAN-шины
На другой стороне модуля находятся 2-контактная винтовая клемма и 2-контактный штыревой разъём для подключения витой пары CAN-шины.
L — сигнал CAN low стандарта CAN-шины.
H — сигнал CAN high стандарта CAN-шины.
Аппаратное подключение
Теперь, когда мы знаем всё о модуле, давайте построим собственную CAN-сеть.
Пример 1: Простая двухузловая CAN-сеть
В этом примере строится простая двухузловая CAN-сеть — один узел передаёт сообщение, другой принимает.
Для начала подключите вывод VCC модуля к 5V на Arduino, а вывод GND — к земле.
Подключим выводы SPI. Обратите внимание, что каждая плата Arduino имеет свой набор выводов SPI, которые должны быть подключены соответственно. Для плат Arduino UNO/Nano V3.0 это выводы digital 13 (SCK), 12 (MISO), 11 (MOSI) и 10 (CS). Если вы используете другую плату Arduino, проверьте официальную документацию по `расположению выводов SPI `_.
Теперь подключите вывод INT модуля к цифровому выводу 2 Arduino.
Вам нужно собрать две такие схемы. Одна будет выступать передатчиком, другая — приёмником. Обе имеют одинаковую разводку.
Соединение модулей простое: CAN L подключается к CAN L, а CAN H — к CAN H. В идеале провода должны быть витой парой, но для простого тестирования на макетной плате или коротких расстояний это не обязательно.
Важно помнить, что с увеличением длины шины или электрических шумов окружающей среды использование витой пары и добавление экранирования становится более важным.
Наконец, установите перемычку на обоих модулях, так как это простая двухузловая CAN-сеть.
Соберите сеть, как показано на схеме.
Пример 2: Многоузловая CAN-сеть
В этом примере строится более крупная CAN-сеть — несколько узлов отправляют сообщения, и один узел ретранслирует их на ПК через последовательный порт.
Другие узлы могут быть добавлены между двумя конечными узлами. Они могут быть врезаны в линию или подключены к основной шине с помощью короткого ответвления, если его длина не превышает 30 см.
Наконец, установите перемычку на первом и последнем узлах CAN-сети и снимите её с промежуточных узлов.
Соберите сеть, как показано на схеме.
Установка библиотеки
Для работы с модулем MCP2515 существует отличная библиотека. Вам нужно скачать и установить её в Arduino IDE.
Для установки библиотеки перейдите в Sketch > Include Library > Manage Libraries… Подождите, пока Менеджер библиотек загрузит индекс и обновит список установленных библиотек.
Отфильтруйте поиск, введя „mcp2515“. Найдите CAN от Sandeep Mistry. Нажмите на запись и выберите Install.
Пример кода Arduino
В этом простом тесте мы попытаемся передать сообщение «Hello World» по CAN-шине, чтобы проверить, можно ли его декодировать. Это поможет вам научиться использовать модули и может послужить основой для более практических экспериментов и проектов.
Код для узла-передатчика
Загрузите этот скетч на узел-передатчик.
Если у вас несколько узлов на CAN-шине, загрузите этот скетч на каждый из узлов-передатчиков. Обязательно измените идентификаторы сообщений на уникальные значения для каждого узла.
#include <CAN.h>
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("CAN Sender");
// 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>
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("CAN Receiver Callback");
// start the CAN bus at 500 kbps
if (!CAN.begin(500E3)) {
Serial.println("Starting CAN failed!");
while (1);
}
// register the receive callback
CAN.onReceive(onReceive);
}
void loop() {
// do nothing
}
void onReceive(int 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();
}
Обратите внимание, что функция loop оставлена пустой, так как скетч использует прерывание для уведомления Arduino о получении и загрузке действительного сообщения в один из приёмных буферов.
Демонстрация
После загрузки скетча откройте монитор последовательного порта на скорости 9600 бод. Узел-передатчик отправляет стандартный и расширенный CAN-пакет каждую секунду.
Узел-приёмник принимает их и передаёт на ПК через последовательный порт.