Browse Source
Add a sample for the UART bridge driver using a USB CDC-ACM UART and a normal one. Demonstrates how to do settings propagation (bitrate changes) and multiple instances. The sample needs to know what specific UART are available on each board, so unfortunately any additional board needs its own overlay. Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>pull/89972/head
8 changed files with 240 additions and 0 deletions
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
cmake_minimum_required(VERSION 3.20.0) |
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) |
||||
project(cdc_acm_bridge) |
||||
|
||||
include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake) |
||||
FILE(GLOB app_sources src/*.c) |
||||
target_sources(app PRIVATE ${app_sources}) |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
# Copyright 2025 Google LLC |
||||
# SPDX-License-Identifier: Apache-2.0 |
||||
|
||||
# Source common USB sample options used to initialize new experimental USB |
||||
# device stack. The scope of these options is limited to USB samples in project |
||||
# tree, you cannot use them in your own application. |
||||
source "samples/subsys/usb/common/Kconfig.sample_usbd" |
||||
|
||||
source "Kconfig.zephyr" |
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
.. zephyr:code-sample:: usb-cdc-acm-bridge |
||||
:name: USB CDC-ACM bridge |
||||
:relevant-api: uart_interface |
||||
|
||||
Use USB CDC-ACM driver to implement a serial port bridge. |
||||
|
||||
Overview |
||||
******** |
||||
|
||||
This sample app demonstrates use of a USB CDC-ACM to bridge a standard hardware |
||||
UART on a supported board. |
||||
This sample can be found under :zephyr_file:`samples/subsys/usb/cdc_acm_bridge` in the |
||||
Zephyr project tree. |
||||
|
||||
Requirements |
||||
************ |
||||
|
||||
This project requires an USB device driver, which is available for multiple |
||||
boards supported in Zephyr. |
||||
|
||||
The board has to have an USB interface as well as hardware UART, the device |
||||
node for the hardware UART is board specific so each supported board needs an |
||||
explicit overlay. |
||||
|
||||
Building and Running |
||||
******************** |
||||
|
||||
Reel Board |
||||
=========== |
||||
|
||||
To see the console output of the app, open a serial port emulator and |
||||
attach it to the USB to TTL Serial cable. Build and flash the project: |
||||
|
||||
.. zephyr-app-commands:: |
||||
:zephyr-app: samples/subsys/usb/cdc_acm_bridge |
||||
:board: reel_board |
||||
:goals: flash |
||||
:compact: |
||||
|
||||
Running |
||||
======= |
||||
|
||||
Plug the board into a host device, for example, a PC running Linux, the board |
||||
should enumerate as a CDC-ACM device, the CDC-ACM input and output should be |
||||
echoed back to the real UART, and configuration changes to the CDC-ACM uart |
||||
such as bitrate will be propagated to the hardware port. |
||||
|
||||
If the hardware port is connected to an on-board debugger then the output |
||||
should be echoed between the two ports, if it's connected to some pins on the |
||||
boards the pins can be shorted together to test that the driver is working |
||||
correctly. |
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
/* |
||||
* Copyright 2025 Google LLC |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
/ { |
||||
uart-bridge0 { |
||||
compatible = "zephyr,uart-bridge"; |
||||
peers = <&cdc_acm_uart0 &arduino_serial>; |
||||
}; |
||||
|
||||
}; |
||||
|
||||
&arduino_serial { |
||||
status = "okay"; |
||||
}; |
||||
|
||||
&zephyr_udc0 { |
||||
cdc_acm_uart0: cdc_acm_uart0 { |
||||
compatible = "zephyr,cdc-acm-uart"; |
||||
label = "Zephyr USB CDC-ACM"; |
||||
}; |
||||
}; |
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/ { |
||||
uart-bridge0 { |
||||
compatible = "zephyr,uart-bridge"; |
||||
peers = <&cdc_acm_uart0 &uart0>; |
||||
}; |
||||
|
||||
uart-bridge1 { |
||||
compatible = "zephyr,uart-bridge"; |
||||
peers = <&cdc_acm_uart1 &uart1>; |
||||
}; |
||||
}; |
||||
|
||||
&pinctrl { |
||||
uart1_default: uart1_default { |
||||
group1 { |
||||
pinmux = <UART1_TX_P4>; |
||||
}; |
||||
group2 { |
||||
pinmux = <UART1_RX_P5>; |
||||
input-enable; |
||||
}; |
||||
}; |
||||
}; |
||||
|
||||
&uart1 { |
||||
current-speed = <115200>; |
||||
status = "okay"; |
||||
pinctrl-0 = <&uart1_default>; |
||||
pinctrl-names = "default"; |
||||
}; |
||||
|
||||
&zephyr_udc0 { |
||||
cdc_acm_uart0: cdc_acm_uart0 { |
||||
compatible = "zephyr,cdc-acm-uart"; |
||||
label = "Zephyr USB CDC-ACM uart0"; |
||||
}; |
||||
|
||||
cdc_acm_uart1: cdc_acm_uart1 { |
||||
compatible = "zephyr,cdc-acm-uart"; |
||||
label = "Zephyr USB CDC-ACM uart1"; |
||||
}; |
||||
}; |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
CONFIG_UART_CONSOLE=n |
||||
|
||||
CONFIG_USB_DEVICE_STACK_NEXT=y |
||||
|
||||
CONFIG_SERIAL=y |
||||
CONFIG_UART_LINE_CTRL=y |
||||
CONFIG_UART_USE_RUNTIME_CONFIGURE=y |
||||
CONFIG_USBD_CDC_ACM_CLASS=y |
||||
|
||||
CONFIG_SAMPLE_USBD_PID=0x0001 |
||||
CONFIG_SAMPLE_USBD_PRODUCT="USBD CDC ACM bridge sample" |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
sample: |
||||
name: CDC ACM USB bridge |
||||
tests: |
||||
sample.usb.cdc-acm-bridge.arduino_serial: |
||||
tags: usb |
||||
build_only: true |
||||
depends_on: usbd arduino_serial |
||||
sample.usb.cdc-acm-bridge.two_devices: |
||||
tags: usb |
||||
build_only: true |
||||
platform_allow: |
||||
- rpi_pico |
@ -0,0 +1,82 @@
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2025 Google LLC |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
|
||||
#include <sample_usbd.h> |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <zephyr/device.h> |
||||
#include <zephyr/drivers/uart/uart_bridge.h> |
||||
#include <zephyr/kernel.h> |
||||
|
||||
#include <zephyr/usb/usb_device.h> |
||||
#include <zephyr/usb/usbd.h> |
||||
#include <zephyr/logging/log.h> |
||||
LOG_MODULE_REGISTER(cdc_acm_bridge, LOG_LEVEL_INF); |
||||
|
||||
const struct device *const uart_dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); |
||||
|
||||
static struct usbd_context *sample_usbd; |
||||
|
||||
#define DEVICE_DT_GET_COMMA(node_id) DEVICE_DT_GET(node_id), |
||||
|
||||
const struct device *uart_bridges[] = { |
||||
DT_FOREACH_STATUS_OKAY(zephyr_uart_bridge, DEVICE_DT_GET_COMMA) |
||||
}; |
||||
|
||||
static void sample_msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) |
||||
{ |
||||
LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); |
||||
|
||||
if (usbd_can_detect_vbus(ctx)) { |
||||
if (msg->type == USBD_MSG_VBUS_READY) { |
||||
if (usbd_enable(ctx)) { |
||||
LOG_ERR("Failed to enable device support"); |
||||
} |
||||
} |
||||
|
||||
if (msg->type == USBD_MSG_VBUS_REMOVED) { |
||||
if (usbd_disable(ctx)) { |
||||
LOG_ERR("Failed to disable device support"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING || |
||||
msg->type == USBD_MSG_CDC_ACM_CONTROL_LINE_STATE) { |
||||
for (uint8_t i = 0; i < ARRAY_SIZE(uart_bridges); i++) { |
||||
/* update all bridges, non valid combinations are
|
||||
* skipped automatically. |
||||
*/ |
||||
uart_bridge_settings_update(msg->dev, uart_bridges[i]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
int main(void) |
||||
{ |
||||
int err; |
||||
|
||||
sample_usbd = sample_usbd_init_device(sample_msg_cb); |
||||
if (sample_usbd == NULL) { |
||||
LOG_ERR("Failed to initialize USB device"); |
||||
return -ENODEV; |
||||
} |
||||
|
||||
if (!usbd_can_detect_vbus(sample_usbd)) { |
||||
err = usbd_enable(sample_usbd); |
||||
if (err) { |
||||
LOG_ERR("Failed to enable device support"); |
||||
return err; |
||||
} |
||||
} |
||||
|
||||
LOG_INF("USB device support enabled"); |
||||
|
||||
k_sleep(K_FOREVER); |
||||
|
||||
return 0; |
||||
} |
Loading…
Reference in new issue