added usb - can bridge (loopback)

This commit is contained in:
2025-08-30 17:34:03 +02:00
commit de2af53284
19 changed files with 455 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.debug
.vscode
build

9
CMakeLists.txt Normal file
View File

@@ -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)

View File

@@ -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";
};

15
config.yml Normal file
View File

@@ -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! (.*)"

43
prj.conf Normal file
View File

@@ -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

12
src/CMakeLists.txt Normal file
View File

@@ -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
)

View File

@@ -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
)

View File

@@ -0,0 +1,175 @@
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/can.h>
#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);
}

View File

@@ -0,0 +1,3 @@
#pragma once
void bridges_init(void);

7
src/canfd/CMakeLists.txt Normal file
View File

@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.20.0)
target_include_directories(app PRIVATE ./)
target_sources(app PRIVATE
can_fd.cpp
)

15
src/canfd/can_fd.cpp Normal file
View File

@@ -0,0 +1,15 @@
#include <zephyr/drivers/can.h>
#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");
}
}

3
src/canfd/can_fd.hpp Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
void canfd_init(void);

View File

@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.20.0)
target_include_directories(app PRIVATE ./)
target_sources(app PRIVATE
devices.cpp
)

40
src/devices/devices.cpp Normal file
View File

@@ -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;
}

15
src/devices/devices.hpp Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
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);

20
src/main.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include <zephyr/kernel.h>
#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());
}

7
src/usb/CMakeLists.txt Normal file
View File

@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.20.0)
target_include_directories(app PRIVATE ./)
target_sources(app PRIVATE
usb_cdc_acm.cpp
)

38
src/usb/usb_cdc_acm.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include <zephyr/usb/usbd.h>
#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);
}

3
src/usb/usb_cdc_acm.hpp Normal file
View File

@@ -0,0 +1,3 @@
#pragma once
void usb_init(void);