Работа с CAN и CAN FD на Arduino
В этом разделе рассматриваются примеры программ для передачи данных по CAN и CAN FD
Пример отправки и получения сообщения по ОБЫЧНОМУ CAN
Напишем на ардуино программу, которая постоянно шлет в CAN пакет, состоящий из 4 байт, а также при получении сообщения извне (будем считать оно тоже состоит не более, чем из 4 байт) отображает его в Serial порт.
Сам файл с программой:
Начнем с классического протокола CAN.
Подключим необходимый файл к нашему скетчу и создадим переменную data - отправляемый пакет.
Класс CanFD - это управляющая структура. Он содержит следующие методы:
init() - метод, инициализирующий CAN
write_default_params_classic() - метод, который запишет параметры по умолчанию для CAN FD (настроены два битрейта 1000000 и 8000000 если вы будете использовать raspberry для чтения/записи в can fd, то при запуске интерфейса используйте команду
sudo ip link set can0 up txqueuelen 65535 type can bitrate
1000000
dbitrate
8000000
fd on.
Подробно о том как работать с CAN на RPI написано в разделе VB Can FD Raspberry HAT)write_default_params() - метод, который запишет параметры по умолчанию для обычного CAN( здесь по умолчанию настроен битрейт на 1000000. Запуск интерфейса на RPI:
sudo ip link set can0 up txqueuelen 65535 type can bitrate 1000000
)
О том, как поменять тайминги и на что они влияют, здесь сказано не будет. Для целей обучения и простого использования мы предлагаем настройки по умолчанию. Если хотите углубиться - добро пожаловать в раздел Коммуникации-FDCAN
apply_config() - метод, который применит записанные ранее методы для нашего экземпляра класса
default_start() - метод, который запускает FD CAN на модуле, он обязателен.
get_hfdcan() - сохраняет всю конфигурацию и возвращает указатель на структуру FDCAN_HandleTypeDef
#include <VBCoreG4_arduino_system.h>
uint8_t data[4] = {222, 173, 190, 239}; //DE AD BE EF
FDCAN_HandleTypeDef* hfdcan1; // создаем указатель на переменную типа FDCAN_HandleTypeDef
CanFD* can; //в классе CanFD реализованы функции init(), запускающая can,
// get_hfdcan(), возвращающая указатель на структуру FDCAN_HandleTypeDef и
//write_default_params() - функ
FDCAN_TxHeaderTypeDef TxHeader; // хидер сообщения, которое будет отправляться
В setup() вызывем функцию SystemClock_Config() - она обязательна для настройки тактирования, методы класса CanFD - init() , инициализирующую can, методы для записи и подтверждения настроек, функцию, запускающую CAN FD и get_hfdcan(), возвращающую инициализированнную управляющую can-ом структуру типа FDCAN_HandleTypeDef. Также укажем в хидере нашего сообщения его ID, размер и тип ID сообщения - он может быть расширенным, а может стандартным.
Если тип ID расширенный, то ID 0x64 будет выглядеть: 00000064, если стандартный: 064. Мы советуем использовать расширенный.
void setup() {
Serial.begin(115200);
pinMode(LED2, OUTPUT);
/* Настройка FD CAN */
SystemClock_Config(); // Настройка тактирования
can = new CanFD(); // Создаем управляющий класс
can->init(); // Инициализация CAN
can->write_default_params_classic(); // Записываем дефолтные параметры для classic CAN
can->apply_config(); // Применяем их
can->default_start(); // Запуск FDCAN модуля
hfdcan1 = can->get_hfdcan(); // Сохраняем конфиг
TxHeader.Identifier = 0x64;
TxHeader.DataLength = FDCAN_DLC_BYTES_4; // длина сообщения 4 байта
TxHeader.IdType = FDCAN_EXTENDED_ID; // тип ID расширенный
}
В основном цикле loop() ставим условие: если в очереди на отправку сообщения есть место, тогда отправляем сообщение. Если пакет отправлен и все хорошо будем мигать светодиодиком.
//------Отправка сообщения в can------
if (HAL_FDCAN_GetTxFifoFreeLevel(hfdcan1) != 0){
if (HAL_FDCAN_AddMessageToTxFifoQ(hfdcan1, &TxHeader, data) != HAL_OK){ Error_Handler(); }
else{digitalWrite(LED2, !digitalRead(LED2));} //помигаем светодиодом, если все ок
}
Чтение пакетов из can происходит следующим образом: пока в очереди получаемых сообщений есть хотя бы одно, создаем хидер этого сообщения и массив, где будет лежать содержимое посылки. Если сообщение получено, выведем в Serial порт его ID, и само сообщение. Сейчас, для простоты, выведем первые 4 байта массива, считая, что больше отправлено не будет.
// -------Получение сообщений из can-------
while(HAL_FDCAN_GetRxFifoFillLevel(hfdcan1, FDCAN_RX_FIFO0) > 0 )
{
FDCAN_RxHeaderTypeDef Header; // хидер для входящего сообщения
uint8_t RxData[4]; // максимальная длина сообщения - 64 байта
if (HAL_FDCAN_GetRxMessage(hfdcan1, FDCAN_RX_FIFO0, &Header, RxData) != HAL_OK){ Serial.println("error"); Error_Handler(); }
else{ // напечатаем первые 4 байта входящего сообщения, если все ок. Пример отправки сообщения cansend can0 00000123#DEADBEEF
Serial.print("ID ");
Serial.print(Header.Identifier); // ID сообщения
Serial.print(" data: ");
Serial.print(RxData[0]);
Serial.print(" ");
Serial.print(RxData[1]);
Serial.print(" ");
Serial.print(RxData[2]);
Serial.print(" ");
Serial.print(RxData[3]);
Serial.println(" ");
}
}
delay(100);
В конце цикла добавили задержку в 100 милисекунд.
Загрузите программу на плату. Проверить действительно ли все сообщения отправляются в can можно на Raspberry с помощью утилиты candump, а чтобы отправить сообщение воспользуйтесь утилитой cansend. О том как работают утилиты было расказано в главе Работа с CAN на Raspberry.
Также вы можете эту программу разделить на две части - отправку и чтение сообщения и загрузить на два модуля (для приемника закомментируйте часть с отправкой сообщения, для передатчика - часть с приемом), а потом проверить через сериал порт, что приемник действительно получает те сообщения, что отправляет ему второй модуль.
Пример отправки и получения сообщения по CAN FD
В целом код будет отличаться только настройками в setup() (названиями вызываемых функций):
Serial.begin(115200);
pinMode(LED2, OUTPUT);
/* Настройка FD CAN
*/
SystemClock_Config(); // Настройка тактирования
canfd = new CanFD(); // Создаем управляющий класс
canfd->init(); // Инициализация CAN
canfd->write_default_params(); // Записываем дефолтные параметры для FDCAN (1000000 nominal / 8000000 data)
canfd->apply_config(); // Применяем их
hfdcan1 = canfd->get_hfdcan(); // Сохраняем конфиг
canfd->default_start();
TxHeader.Identifier = 0x68;
TxHeader.DataLength = FDCAN_DLC_BYTES_4;
TxHeader.IdType = FDCAN_EXTENDED_ID;
Если у вас возникла ситуация, в которой одно устройство отправляет сообщения, а другое не принимает, почитайте как можно справиться с этой ошибкой здесь
Last updated