Работа с CAN FD на Ардуино

Напишем на ардуино программу, которая постоянно шлет в CAN пакет, состоящий из 4 байт, а также при получении сообщения извне (будем считать оно тоже состоит не более, чем из 4 байт) отображает его в Serial порт. Подключим необходимый файл к нашему скетчу и создадим переменную data - отправляемый пакет. Для замера времени создадим переменную t.

#include <VBCoreG4_arduino_system.h>


uint8_t data[4] = {222, 173, 190, 239}; //байты - DE AD BE EF
unsigned long t;
FDCAN_HandleTypeDef  hfdcan1;// создаем переменную типа FDCAN_HandleTypeDef
CanFD canfd; //в классе CanFD реализованы функции can_init(), запускающая can, 
             // get_hfdcan(), возвращающая ссылку на структуру FDCAN_HandleTypeDef и
             // create_header, создающая хидер для отправляемого сообщения 

В setup() вызывем методы класса CanFD - can_init() , инициализирующую can и get_hfdcan(), возвращающую инициализированнную управляющую can-ом структуру типа FDCAN_HandleTypeDef

void setup() {
   Serial.begin(115200);
   canfd.can_init(); // запускаем can
   hfdcan1 = *(canfd.get_hfdcan()); 
}

В основном цикле loop() ставим условие: если в очереди на отправку сообщения есть место, тогда создадим хидер отправляемого пакета. В классе CanFD для этого есть метод create_header(uint32_t ID), где ID - это ID сообщения. Для примера пусть у нас ID = 100 (0х64). Указываем длину сообщения и отправляем. Если пакет отправлен и все хорошо будем мигать светодиодиком.

 
 if (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) != 0){
       FDCAN_TxHeaderTypeDef TxHeader = create_header(100); //создаем хидер исходящего сообщения, 100 - ID сообщения, в hex 0х64
       TxHeader.DataLength = FDCAN_DLC_BYTES_4; //количество байт в сообщении - 4
      if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, data) != HAL_OK){ Error_Handler(); } 
      else{digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));} //помигаем светодиодом, если все ок
    }

Чтение пакетов из can происходит следующим образом: пока в очереди получаемых сообщений есть хотя бы одно, создаем хидер этого сообщения и массив, где будет лежать содержимое посылки. Если сообщение получено, выведем в Serial порт его ID, и само сообщение. Сейчас, для простоты, выведем первые 4 байта массива, считая, что больше отправлено не будет.

while(HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) > 0 )
  {
    FDCAN_RxHeaderTypeDef Header;  // хидер для входящего сообщения
    uint8_t RxData[64]; // максимальная длина сообщения - 64 байта 
    if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &Header, RxData) != HAL_OK){ 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("  ");
  }

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

#include <VBCoreG4_arduino_system.h>


//в VBCoreG4_arduino_system.h пин PA5 определен как LED2 

//запустить can c распберри - sudo ip link set can0 up txqueuelen 65535 type can bitrate 1000000 dbitrate 8000000 fd on
//отправить сообщение c распберри - cansend can0 00000123#DEADBEEF, ID всегда содержит 8 цифр
//прочитать все сообщения в can -  candump can0
//прочитать сообщение can по его ID -  candump can0,ID:7ff

//в libraries добавить библиотеку VBCoreG4_arduino_system
//функция can_init() запускает can
//функция get_hfdcan() возвращает переменную типа FDCAN_HandleTypeDef, без которой невозможно взаимодействие с can
//функция create_header(uint8_t ID) создает хидер для отправки сообщения, в нее нужно передать переменную типа uint8_t - ID сообщения


uint8_t data[4] = {222, 173, 190, 239}; //DE AD BE EF
unsigned long t;
FDCAN_HandleTypeDef  hfdcan1; // создаем переменную типа FDCAN_HandleTypeDef
CanFD canfd;
void setup() {
   Serial.begin(115200);
   canfd.can_init(); // запускаем can
   hfdcan1 = *(canfd.get_hfdcan()); 
}


void loop() {
    t = micros(); //время в микросекундах
  //------Отправка сообщения в can------
  
    if (HAL_FDCAN_GetTxFifoFreeLevel(&hfdcan1) != 0){
       FDCAN_TxHeaderTypeDef TxHeader = canfd.create_header(100); //создаем хидер исходящего сообщения, 100 - ID сообщения, в hex 0х64
       TxHeader.DataLength = FDCAN_DLC_BYTES_4; //количество байт в сообщении - 4
      if (HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, data) != HAL_OK){ Error_Handler(); } 
      else{digitalWrite(LED2, !digitalRead(LED2));} //помигаем светодиодом, если все ок
    }

// -------Получение сообщений из can-------

while(HAL_FDCAN_GetRxFifoFillLevel(&hfdcan1, FDCAN_RX_FIFO0) > 0 )
  {
    FDCAN_RxHeaderTypeDef Header;  // хидер для входящего сообщения
    uint8_t RxData[64]; // максимальная длина сообщения - 64 байта 
    if (HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &Header, RxData) != HAL_OK){ 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("  ");
  }
  }
  delayMicroseconds(1000-(micros()-t)); //время выполнения программы 1 мс
  
}

Загрузите программу на плату. Проверить действительно ли все сообщения отправляются в can можно на Raspberry с помощью утилиты candump, а чтобы отправить сообщение воспользуйтесь утилитой cansend. О том как работают утилиты было расказано в предыдущей главе.

Last updated