При последующей настройке скорости передачи данных CAN и режима loopback необходимо использовать команду ip. Однако при использовании команды ip, скомпилированной по умолчанию в PetaLinux, появляется следующее сообщение об ошибке:
xxxxxxxxxxip: either "dev" is duplicate, or "type" is garbage
Согласно инструкциям в справочном материале, компонент iproute2 должен быть правильно настроен в rootfs для получения полнофункциональной поддержки команды ip:
xxxxxxxxxxpetalinux-config -c rootfs
#В menuconfig для корневой файловой системы перейдите в указанное ниже место и добавьте iproute2:Filesystem Packages ---> base ---> iproute2 ---> [*] iproute2Справочный материал:
65243 - PetaLinux 2014.4 - Команда "ip" не может правильно найти устройства CAN
После завершения предыдущего шага 'petalinux-config -c rootfs' используйте следующие команды для перекомпиляции и упаковки:
xxxxxxxxxxpetalinux-build
petalinux-package --boot --fsbl ./images/linux/zynq_fsbl.elf --u-boot --fpga --forceОчистите содержимое второго раздела TF-карты и извлеките файл ./image/rootfs.tar.gz во второй раздел TF-карты с помощью следующей команды:
xxxxxxxxxxsudo tar -xzf ./images/linux/rootfs.tar.gz -C /media/mind/rootfs/syncВставьте TF-карту в плату, выберите режим загрузки с SD-карты с помощью перемычки, подключите последовательный порт к плате и включите питание.
Щелкните правой кнопкой мыши на src, чтобы создать новую папку:
После создания соответствующих файлов .c и .h это должно выглядеть следующим образом:
Добавьте папку, содержащую файлы .h, в путь:
Откройте канал CAN и установите скорость передачи данных с помощью команд. Соответствующие команды следующие:
xxxxxxxxxx #Проверить, добавлен ли can0 в сетевые устройства: ifconfig -a #Установить скорость передачи can0, здесь установлено 100 кбит/с ip link set can0 type can bitrate 100000 #После установки запросить параметры устройства can0 следующей командой ip -details link show can0 #После завершения настройки включить устройство can0 следующей командой ifconfig can0 up #Использовать следующую команду для отключения устройства can0 ifconfig can0 down #Во время работы устройства использовать следующую команду для запроса рабочего состояния ip -d -s link show can0 #Установить can0 в режим loopback, самопередача и самоприем ip link set can0 up type can loopback on
#Скорость передачи и режим можно установить вместе ip link set can0 type can bitrate 200000 loopback on ip link set can0 type can bitrate 200000 loopback off #-e означает расширенный кадр, CAN_ID максимум 29 бит, стандартный кадр CAN_ID максимум 11 бит, -i означает CAN_ID # 0x800 соответствует ID кадра, 0x11~0x88 соответствует передаваемым данным cansend can0 -i 0x800 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 -e #--loop означает отправку 20 пакетов cansend can0 -i 0x02 0x11 0x12--loop=20 #Принять данные CAN0 candump can0При изменении рабочего состояния CAN необходимо сначала выключить can0, иначе будет выведено сообщение "Device or resource busy"
Система Linux управляет устройствами CAN как сетевыми устройствами и предоставляет интерфейс SocketCAN, что делает связь по шине CAN похожей на Ethernet, с более универсальными и гибкими интерфейсами для разработки приложений.
Инициализация и настройка CAN выполняются с помощью системного вызова system() для выполнения соответствующих команд, упомянутых ранее.
xxxxxxxxxxbool can_open(CAN_Handle_t *h, const char *ifname){ uint8_t cmd_cfg[64]; int ret = -1;
can_close(h); can_is_up = false; can_send_active = false; can_rcv_status = false;
sprintf(cmd_cfg, "ip link set can0 type can bitrate %d loopback %s", can_baud_rate, (can_mode == 0) ?"off":"on"); ret = system(cmd_cfg);
if (ret != -1) { ret = system("ip link set can0 up"); }
if (ret == -1) { perror("open can interface failed"); return false; }
struct ifreq ifr; struct sockaddr_can addr;
if (!h || !ifname) return false;
h->sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (h->sockfd < 0) { perror("socket(PF_CAN)"); return false; }
strncpy(h->ifname, ifname, sizeof(h->ifname) - 1);
memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); if (ioctl(h->sockfd, SIOCGIFINDEX, &ifr) < 0) { perror("ioctl(SIOCGIFINDEX)"); close(h->sockfd); h->sockfd = -1; return false; }
addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex;
if (bind(h->sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind"); close(h->sockfd); h->sockfd = -1; return false; }
can_is_up = true; can_send_active = false; can_rcv_status = false; return true;}
void can_close(CAN_Handle_t *h){ const char *cmd = {"ip link set can0 down"}; int ret = system(cmd); if (ret == -1) { perror("system"); }
if (h && h->sockfd >= 0) { close(h->sockfd); h->sockfd= -1; }}Большинство структур данных и функций в SocketCAN определены в linux/can.h, а создание сокетов шины CAN выполняется с использованием стандартных сетевых сокетов.
Каждый раз, когда шина CAN получает данные, они передаются в виде единиц can_frame. Эта структура определена следующим образом:
xxxxxxxxxxstruct canfd_frame { canid_t can_id; /* 32-битный CAN_ID + флаги EFF/RTR/ERR */ __u8 len; /* длина полезной нагрузки кадра в байтах */ __u8 flags; /* дополнительные флаги для CAN FD */ __u8 __res0; /* зарезервировано / выравнивание */ __u8 __res1; /* зарезервировано / выравнивание */ __u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8)));};can_id — это идентификатор кадра. При отправке стандартного кадра используются младшие 11 бит can_id; если это расширенный кадр, используются биты 0-28. 29-й, 30-й и 31-й биты can_id являются битами-идентификаторами кадра, используемыми для определения типа кадра, как показано ниже:
xxxxxxxxxx/* флаги описания специального адреса для CAN_ID *//* EFF/SFF устанавливается в старшем значащем бите (MSB) *//* запрос на удаленную передачу *//* кадр сообщения об ошибке */Передача данных использует функцию write. Чтобы отправить кадр данных с идентификатором 0x123 и данными в один байт 0xAB, используется следующий метод:
xxxxxxxxxxstruct can_frame frame;frame.can_id = 0x123;frame.can_dlc = 1;frame.data[0] = 0xAB;int nbytes = write(s, &frame, sizeof(frame));if(nbytes != sizeof(frame)) printf("Error\n");При отправке удаленного кадра, frame.can_id = CAN_RTR_FLAG | 0x123
Прием данных использует функцию read, реализованную следующим образом:
xxxxxxxxxxstruct can_frame frame;int nbytes = read(s, &frame, sizeof(frame))При тестировании программ связи CAN в Linux обычно необходимо создавать поток, отвечающий за прием. Использование потоков позволяет в полной мере использовать планирование задач и повторное использование ресурсов, предоставляемые ядром Linux.
pthread_t recv_tid;pthread_create(&recv_tid, NULL, can_recv_thread, &g_can_handle);
void *can_recv_thread(void *arg){ CAN_Handle_t *h = (CAN_Handle_t *)arg; uint32_t id = 0;
while (1) { if (can_is_up && (can_recv(h, &id, can_rcv_buf, &can_rcv_len) > 0)) { printf("Received CAN frame: ID=0x%X, Data=", id); for (int i = 0; i < can_rcv_len; i++) { printf("%02X ", can_rcv_buf[i]); } can_rcv_status = true; printf("\n"); } else { usleep(1000); } }
printf("CAN receive thread exiting.\n"); return NULL;}