From de2af53284b35f56f3b84b11b8c6d01569fd4b1f Mon Sep 17 00:00:00 2001 From: Timo Date: Sat, 30 Aug 2025 17:34:03 +0200 Subject: [PATCH] added usb - can bridge (loopback) --- .gitignore | 3 + CMakeLists.txt | 9 + boards/nucleo_h755zi_q_stm32h755xx_m7.overlay | 33 ++++ config.yml | 15 ++ prj.conf | 43 +++++ src/CMakeLists.txt | 12 ++ src/bridges/CMakeLists.txt | 7 + src/bridges/bridge_usb_cdc_acm_canfd.cpp | 175 ++++++++++++++++++ src/bridges/bridge_usb_cdc_acm_canfd.hpp | 3 + src/canfd/CMakeLists.txt | 7 + src/canfd/can_fd.cpp | 15 ++ src/canfd/can_fd.hpp | 3 + src/devices/CMakeLists.txt | 7 + src/devices/devices.cpp | 40 ++++ src/devices/devices.hpp | 15 ++ src/main.cpp | 20 ++ src/usb/CMakeLists.txt | 7 + src/usb/usb_cdc_acm.cpp | 38 ++++ src/usb/usb_cdc_acm.hpp | 3 + 19 files changed, 455 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 boards/nucleo_h755zi_q_stm32h755xx_m7.overlay create mode 100644 config.yml create mode 100644 prj.conf create mode 100644 src/CMakeLists.txt create mode 100644 src/bridges/CMakeLists.txt create mode 100644 src/bridges/bridge_usb_cdc_acm_canfd.cpp create mode 100644 src/bridges/bridge_usb_cdc_acm_canfd.hpp create mode 100644 src/canfd/CMakeLists.txt create mode 100644 src/canfd/can_fd.cpp create mode 100644 src/canfd/can_fd.hpp create mode 100644 src/devices/CMakeLists.txt create mode 100644 src/devices/devices.cpp create mode 100644 src/devices/devices.hpp create mode 100644 src/main.cpp create mode 100644 src/usb/CMakeLists.txt create mode 100644 src/usb/usb_cdc_acm.cpp create mode 100644 src/usb/usb_cdc_acm.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77174c8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.debug +.vscode +build \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6c4bcf9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.20.0) + +SET(CMAKE_CXX_COMPILER /opt/zephyr-sdk/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(usb_cdc_can_fd_bridge C CXX) + +add_subdirectory(src) \ No newline at end of file diff --git a/boards/nucleo_h755zi_q_stm32h755xx_m7.overlay b/boards/nucleo_h755zi_q_stm32h755xx_m7.overlay new file mode 100644 index 0000000..1455e38 --- /dev/null +++ b/boards/nucleo_h755zi_q_stm32h755xx_m7.overlay @@ -0,0 +1,33 @@ +/ { + +}; + +&zephyr_udc0 { + status = "okay"; + + cdc_acm_uart0: cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + label = "CDC_ACM_0"; + }; + + cdc_acm_uart1: cdc_acm_uart1 { + compatible = "zephyr,cdc-acm-uart"; + label = "CDC_ACM_1"; + }; + + cdc_acm_uart2: cdc_acm_uart2 { + compatible = "zephyr,cdc-acm-uart"; + label = "CDC_ACM_2"; + }; + + cdc_acm_uart3: cdc_acm_uart3 { + compatible = "zephyr,cdc-acm-uart"; + label = "CDC_ACM_3"; + }; +}; + +&fdcan1 { + bitrate = <1000000>; + bitrate-data = <4000000>; + status = "okay"; +}; \ No newline at end of file diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..b57c1aa --- /dev/null +++ b/config.yml @@ -0,0 +1,15 @@ +sample: + description: USB CDC - CAN FD Bridge + name: ros2_node +common: + min_ram: 2 + min_flash: 16 + tags: usb_cdc + integration_platforms: + - nucleo_h563zi + - nucleo_h755zi_q + harness: console + harness_config: + type: one_line + regex: + - "Hello World! (.*)" diff --git a/prj.conf b/prj.conf new file mode 100644 index 0000000..fc05873 --- /dev/null +++ b/prj.conf @@ -0,0 +1,43 @@ +# c version +CONFIG_MAIN_STACK_SIZE=32768 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=8192 +CONFIG_ISR_STACK_SIZE=2048 +CONFIG_USBD_THREAD_STACK_SIZE=4096 +CONFIG_CPP=y +CONFIG_NEWLIB_LIBC=y +CONFIG_NEWLIB_LIBC_NANO=n +CONFIG_PICOLIBC=n +CONFIG_MINIMAL_LIBC=n +CONFIG_POSIX_API=y + +#threads +CONFIG_INIT_STACKS=y +CONFIG_THREAD_STACK_INFO=y +CONFIG_DEBUG=y +CONFIG_DEBUG_THREAD_INFO=y + +CONFIG_CONSOLE=y + +# logging +CONFIG_LOG=y +CONFIG_LOG_MODE_IMMEDIATE=y +CONFIG_STDOUT_CONSOLE=y +CONFIG_PRINTK=y +CONFIG_LOG_DEFAULT_LEVEL=3 + +# Enable CDC ACM (virtual COM port) +CONFIG_SERIAL=y +CONFIG_UART_LINE_CTRL=y +CONFIG_USBD_CDC_ACM_CLASS=y +CONFIG_USB_DEVICE_STACK_NEXT=y +CONFIG_USBD_CDC_ACM_WORKQUEUE=y +CONFIG_UDC_WORKQUEUE=y +CONFIG_UDC_STM32=y +CONFIG_UDC_STM32_STACK_SIZE=2048 +CONFIG_UDC_WORKQUEUE_STACK_SIZE=2048 + +# can FD +CONFIG_CAN=y +CONFIG_CAN_FD_MODE=y +CONFIG_CAN_STM32_FDCAN=y + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..c849cc4 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.20.0) + +target_include_directories(app PRIVATE ./) + +add_subdirectory(./bridges) +add_subdirectory(./canfd) +add_subdirectory(./devices) +add_subdirectory(./usb) + +target_sources(app PRIVATE + main.cpp +) \ No newline at end of file diff --git a/src/bridges/CMakeLists.txt b/src/bridges/CMakeLists.txt new file mode 100644 index 0000000..77eac14 --- /dev/null +++ b/src/bridges/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20.0) + +target_include_directories(app PRIVATE ./) + +target_sources(app PRIVATE + bridge_usb_cdc_acm_canfd.cpp +) \ No newline at end of file diff --git a/src/bridges/bridge_usb_cdc_acm_canfd.cpp b/src/bridges/bridge_usb_cdc_acm_canfd.cpp new file mode 100644 index 0000000..687fc9a --- /dev/null +++ b/src/bridges/bridge_usb_cdc_acm_canfd.cpp @@ -0,0 +1,175 @@ +#include +#include + +#include "bridge_usb_cdc_acm_canfd.hpp" +#include "devices.hpp" + +#define USB_THREAD_STACK_SIZE 1024 +K_THREAD_STACK_DEFINE(bridge_stack, USB_THREAD_STACK_SIZE); +struct k_thread bridge_thread_data; +K_FIFO_DEFINE(usb_fifo_0); +K_FIFO_DEFINE(usb_fifo_1); +K_FIFO_DEFINE(usb_fifo_2); +K_FIFO_DEFINE(usb_fifo_3); + +struct uart_msg { + void *fifo_reserved; + uint8_t len; + uint8_t *data; +}; + +uint8_t get_can_id_from_usb_dev(const struct device *dev) { + if (dev == cdc0) { + return 0x10; + } + + if (dev == cdc1) { + return 0x20; + } + + if (dev == cdc2) { + return 0x30; + } + + if (dev == cdc3) { + return 0x40; + } + + return 0x80; +} + +struct k_fifo *get_fifo_from_dev(const struct device *dev) { + if (dev == cdc0) { + return &usb_fifo_0; + } + + if (dev == cdc1) { + return &usb_fifo_1; + } + + if (dev == cdc2) { + return &usb_fifo_2; + } + + if (dev == cdc3) { + return &usb_fifo_3; + } + + return NULL; +} + +const struct device *get_usb_dev_from_can_id(uint8_t can_id) { + switch (can_id) { + case 0x10: + return cdc0; + case 0x21: + return cdc1; + case 0x31: + return cdc2; + case 0x41: + return cdc3; + default: + return NULL; // unknown + } +} + +uint8_t get_lenght_from_dlc(uint8_t dlc) { + static const uint8_t dlc_to_len[16] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64 + }; + + if (dlc < 16) { + return dlc_to_len[dlc]; + } else { + return 0; + } +} + +static void uart_rx_handler(const struct device *dev, void *user_data) +{ + ARG_UNUSED(user_data); + + while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { + printk("tx: %i, rx: %i\r\n", uart_irq_tx_ready(dev), uart_irq_rx_ready(dev)); + if (uart_irq_rx_ready(dev)) { + uint8_t buffer[64] = {0}; + + int recv_len = uart_fifo_read(dev, buffer, 64); + if (recv_len > 0) { + struct can_frame counter_frame = { + .id = get_can_id_from_usb_dev(dev), + .dlc = can_bytes_to_dlc(recv_len), + .flags = CAN_FRAME_FDF | CAN_FRAME_BRS, + .data = {0}, + }; + memcpy(counter_frame.data, buffer, recv_len); + + can_send(fdcan1, &counter_frame, K_NO_WAIT, NULL, NULL); + } + } + if (uart_irq_tx_ready(dev) >= 64) { + k_fifo* fifo = get_fifo_from_dev(dev); + if (fifo == NULL) { + uart_irq_tx_disable(dev); + return; + } + + struct uart_msg *msg = (uart_msg*) k_fifo_get(fifo, K_MSEC(10)); + if(msg){ + printk("sending to UART: %s / %i\n\r", msg->data, msg->len); + uart_fifo_fill(dev, msg->data, msg->len); + k_free(msg->data); + k_free(msg); + } + else { + printk("Nothing to do\n\r"); + uart_irq_tx_disable(dev); + } + } + } +} + +void can_rx_handler(const struct device *dev, struct can_frame *frame, void *user_data) +{ + ARG_UNUSED(dev); + ARG_UNUSED(user_data); + + const struct device *uart_dev = get_usb_dev_from_can_id(frame->id); + if (!uart_dev) return; + + struct uart_msg *msg = (uart_msg*)k_malloc(sizeof(uart_msg)); + if (!msg) return; + + msg->len = get_lenght_from_dlc(frame->dlc); + printk("msg size: %i / %i\n\r", msg->len, frame->dlc); + + msg->data = (uint8_t*)k_malloc(sizeof(uint8_t) * msg->len); + if (!msg->data) return; + + memset(msg->data, 0, msg->len); + memcpy(msg->data, frame->data, msg->len); + + printk("sending to Queue: %s / %s / %i / %i\n\r", frame->data, msg->data, msg->len, frame->dlc); + k_fifo_put(get_fifo_from_dev(uart_dev), msg); + uart_irq_tx_enable(uart_dev); +} + + +void bridges_init() { + uart_irq_callback_set(cdc0, uart_rx_handler); + uart_irq_rx_enable(cdc0); + uart_irq_callback_set(cdc1, uart_rx_handler); + uart_irq_rx_enable(cdc1); + uart_irq_callback_set(cdc2, uart_rx_handler); + uart_irq_rx_enable(cdc2); + uart_irq_callback_set(cdc3, uart_rx_handler); + uart_irq_rx_enable(cdc3); + + struct can_filter filter = { + .id = 0, // match all IDs + .mask = 0, // accept everything + .flags = 0, + }; + + can_add_rx_filter(fdcan1, can_rx_handler, NULL, &filter); +} \ No newline at end of file diff --git a/src/bridges/bridge_usb_cdc_acm_canfd.hpp b/src/bridges/bridge_usb_cdc_acm_canfd.hpp new file mode 100644 index 0000000..54615e2 --- /dev/null +++ b/src/bridges/bridge_usb_cdc_acm_canfd.hpp @@ -0,0 +1,3 @@ +#pragma once + +void bridges_init(void); \ No newline at end of file diff --git a/src/canfd/CMakeLists.txt b/src/canfd/CMakeLists.txt new file mode 100644 index 0000000..6e03fd8 --- /dev/null +++ b/src/canfd/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20.0) + +target_include_directories(app PRIVATE ./) + +target_sources(app PRIVATE + can_fd.cpp +) \ No newline at end of file diff --git a/src/canfd/can_fd.cpp b/src/canfd/can_fd.cpp new file mode 100644 index 0000000..25d4346 --- /dev/null +++ b/src/canfd/can_fd.cpp @@ -0,0 +1,15 @@ +#include + +#include "can_fd.hpp" +#include "devices.hpp" + + +void canfd_init() { + if (!can_set_mode(fdcan1, CAN_MODE_LOOPBACK | CAN_MODE_FD)) { + printk("CAN0 set to CAN FD\n"); + } + + if (can_start(fdcan1) < 0) { + printk("CAN0 not enabled\n"); + } +} \ No newline at end of file diff --git a/src/canfd/can_fd.hpp b/src/canfd/can_fd.hpp new file mode 100644 index 0000000..87fdd9b --- /dev/null +++ b/src/canfd/can_fd.hpp @@ -0,0 +1,3 @@ +#pragma once + +void canfd_init(void); \ No newline at end of file diff --git a/src/devices/CMakeLists.txt b/src/devices/CMakeLists.txt new file mode 100644 index 0000000..63bbf93 --- /dev/null +++ b/src/devices/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20.0) + +target_include_directories(app PRIVATE ./) + +target_sources(app PRIVATE + devices.cpp +) \ No newline at end of file diff --git a/src/devices/devices.cpp b/src/devices/devices.cpp new file mode 100644 index 0000000..ac86ed4 --- /dev/null +++ b/src/devices/devices.cpp @@ -0,0 +1,40 @@ +#include "devices.hpp" + +const struct device *cdc0 = DEVICE_DT_GET(DT_NODELABEL(cdc_acm_uart0)); +const struct device *cdc1 = DEVICE_DT_GET(DT_NODELABEL(cdc_acm_uart1)); +const struct device *cdc2 = DEVICE_DT_GET(DT_NODELABEL(cdc_acm_uart2)); +const struct device *cdc3 = DEVICE_DT_GET(DT_NODELABEL(cdc_acm_uart3)); + +const struct device *fdcan1 = DEVICE_DT_GET(DT_NODELABEL(fdcan1)); + + +bool verify_are_devices_available(void) { + bool devices_ready = true; + + if (!device_is_ready(cdc0)) { + printk("cdc0 not ready\n"); + devices_ready = false; + } + + if (!device_is_ready(cdc1)) { + printk("cdc1 not ready\n"); + devices_ready = false; + } + + if (!device_is_ready(cdc2)) { + printk("cdc2 not ready\n"); + devices_ready = false; + } + + if (!device_is_ready(cdc3)) { + printk("cdc3 not ready\n"); + devices_ready = false; + } + + if (!device_is_ready(fdcan1)) { + printk("fdcan1 not ready\n"); + devices_ready = false; + } + + return devices_ready; +} \ No newline at end of file diff --git a/src/devices/devices.hpp b/src/devices/devices.hpp new file mode 100644 index 0000000..d23558d --- /dev/null +++ b/src/devices/devices.hpp @@ -0,0 +1,15 @@ +#pragma once +#include +#include +#include + + +extern const struct device *cdc0; +extern const struct device *cdc1; +extern const struct device *cdc2; +extern const struct device *cdc3; + +extern const struct device *fdcan1; + + +bool verify_are_devices_available(void); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..b457fba --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,20 @@ +#include + +#include "devices.hpp" +#include "can_fd.hpp" +#include "usb_cdc_acm.hpp" +#include "bridge_usb_cdc_acm_canfd.hpp" + +int main(void) +{ + if (!verify_are_devices_available()) { + printk("Not all devices ready. Aborting ...\r\n"); + return -1; + } + + canfd_init(); + usb_init(); + bridges_init(); + + k_thread_suspend(k_current_get()); +} \ No newline at end of file diff --git a/src/usb/CMakeLists.txt b/src/usb/CMakeLists.txt new file mode 100644 index 0000000..600c47a --- /dev/null +++ b/src/usb/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20.0) + +target_include_directories(app PRIVATE ./) + +target_sources(app PRIVATE + usb_cdc_acm.cpp +) \ No newline at end of file diff --git a/src/usb/usb_cdc_acm.cpp b/src/usb/usb_cdc_acm.cpp new file mode 100644 index 0000000..c47ed78 --- /dev/null +++ b/src/usb/usb_cdc_acm.cpp @@ -0,0 +1,38 @@ +#include + +#include "usb_cdc_acm.hpp" + +USBD_DEVICE_DEFINE(usb0, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), 0x2fe3, 0xffff); +USBD_DESC_LANG_DEFINE(usb0_lang); +USBD_DESC_MANUFACTURER_DEFINE(usb0_mfr, "Ohmman & Co. KG"); +USBD_DESC_PRODUCT_DEFINE(usb0_prod, "USB Test"); + +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); +USBD_CONFIGURATION_DEFINE(usb0_conf, USB_SCD_SELF_POWERED, 100, &fs_cfg_desc); + +static const char *const usb0_blocklist[] = { + "dfu_dfu", + NULL, +}; + +void usb_init(void) { + usbd_add_descriptor(&usb0, &usb0_lang); + usbd_add_descriptor(&usb0, &usb0_mfr); + usbd_add_descriptor(&usb0, &usb0_prod); + //usbd_add_descriptor(&usb0, &usb0_sn); + + if (usbd_add_configuration(&usb0, USBD_SPEED_FS, &usb0_conf)) { + printk("Failed to add Full-Speed configuration\r\n"); + return; + } + + if (usbd_register_all_classes(&usb0, USBD_SPEED_FS, 1, usb0_blocklist)) { + printk("Failed to register usb classes\r\n"); + return; + } + + usbd_device_set_code_triple(&usb0, USBD_SPEED_FS, USB_BCC_APPLICATION, 0x02, 0x01); + + usbd_init(&usb0); + usbd_enable(&usb0); +} \ No newline at end of file diff --git a/src/usb/usb_cdc_acm.hpp b/src/usb/usb_cdc_acm.hpp new file mode 100644 index 0000000..f7c8fa0 --- /dev/null +++ b/src/usb/usb_cdc_acm.hpp @@ -0,0 +1,3 @@ +#pragma once + +void usb_init(void); \ No newline at end of file