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

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

```arduino
#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

```arduino
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). Указываем длину сообщения и отправляем. Если пакет отправлен и все хорошо будем мигать светодиодиком.

```arduino
 
 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 байта массива, считая, что больше отправлено не будет.

```arduino
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 милисекунды. Весь листинг программы приведен ниже.

```arduino
#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. О том как работают утилиты было расказано в [предыдущей главе](/upravlenie-elektrodvigatelem/kollektornyi-dvigatel/shina-can/praktika-realizaciya-obmena-dannymi-mezhdu-vb32g4-i-raspberry-pi-po-shine-can/nastroika-can-na-rpi.md).&#x20;


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://voltbro.gitbook.io/upravlenie-elektrodvigatelem/kollektornyi-dvigatel/shina-can/praktika-realizaciya-obmena-dannymi-mezhdu-vb32g4-i-raspberry-pi-po-shine-can/rabota-s-can-fd-na-arduino.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
