TinyUSB Printer 与 CDC 复合设备示例详解:双向数据桥接与 IEEE 1284 打印机实现
嵌入式驱动开发通信物联网【免费下载链接】tinyusbAn open source cross-platform USB stack for embedded system项目地址https://gitcode.com/gh_mirrors/ti/tinyusb点击查看免费下载导读本文深入剖析 TinyUSB 仓库中examples/device/printer_to_cdc示例一个同时暴露USB Printer 类接口与CDC 串行接口的复合设备并实现两者之间的双向数据转发。读者将掌握打印机类复合设备的描述符组织方式、tusb_config.h中 CDC 与 Printer 的缓冲区配置、底层 Printer 类驱动的类请求处理机制GET_DEVICE_ID / GET_PORT_STATUS / SOFT_RESET以及在 Linux 主机上使用usblp内核模块完成双向连通性验证的完整实战流程。示例概述一台打印机 串口的桥接设备该示例构建了一个 USB 复合设备同时包含 Printer 类接口和 CDC 串口接口两者之间双向转发数据主机端写入Printer 接口的数据被设备转发到CDC 串口Printer → CDC主机端写入CDC 串口的数据被设备转发到Printer IN 端点CDC → Printer。这种拓扑非常适合调试打印机类通信——例如抓取打印数据流、验证打印机驱动行为也可以作为实现自有打印机类设备时的参考起点。核心代码位于 examples/device/printer_to_cdc/src/main.c全部转发逻辑由两个轻量任务函数完成详见下文双向转发实现。USB 描述符结构CDCIAD Printer 的复合布局接口总览设备配置描述符由三个逻辑接口构成与 examples/device/printer_to_cdc/README.md 中的表格一致接口号类描述0CDC ACM虚拟串口控制接口含中断通知端点1CDC Data串口数据接口由 IAD 与接口 0 关联2PrinterUSB 打印机双向协议 2两个 Bulk 端点接口编号枚举定义在 examples/device/printer_to_cdc/src/usb_descriptors.hITF_NUM_CDC、ITF_NUM_CDC_DATA、ITF_NUM_PRINTER配置描述符总长度通过CONFIG_TOTAL_LEN宏累加计算#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN TUD_CDC_DESC_LEN TUD_PRINTER_DESC_LEN)配置描述符宏展开设备描述符中bDeviceClass TUSB_CLASS_MISC、bDeviceSubClass MISC_SUBCLASS_COMMON、bDeviceProtocol MISC_PROTOCOL_IAD这是复合设备使用 IADInterface Association Descriptor关联 CDC 两个接口所必需的标准写法见 examples/device/printer_to_cdc/src/usb_descriptors.c。实际配置描述符在 usb_descriptors.c 中由两个模板宏拼接TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), TUD_PRINTER_DESCRIPTOR(ITF_NUM_PRINTER, 5, EPNUM_PRINTER_OUT, EPNUM_PRINTER_IN, 64),TUD_CDC_DESCRIPTOR宏定义于 src/device/usbd.h自动展开为 IAD CDC 控制接口 Header/Call/ACM/Union 功能描述符 中断通知端点 数据接口 两个 Bulk 端点因此一个宏调用就吃掉了两个接口编号_itfnum和_itfnum 1。TUD_PRINTER_DESCRIPTOR定义于 src/device/usbd.h展开为一个标准接口描述符加两个 Bulk 端点9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_PRINTER, 1, 2, _stridx, 7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, ..., 0, 7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, ..., 0注意其中的类子类为1、协议为2——对应 USB 打印机类规范中的双向打印机协议Bidirectional, protocol 2意味着设备同时具备 Printer OUT主机→设备和 Printer IN设备→主机两条 Bulk 通道这正是本示例 CDC→Printer 方向转发的前提。端点分配端点编号在 usb_descriptors.c 中按 MCU 类型分支选择通用 MCU默认路径CDC 通知0x81、CDC OUT0x02、CDC IN0x82、Printer OUT0x03、Printer IN0x83LPC17xx/40xx 系列因端点类型由编号固定改为0x02/0x82CDC 数据0x05/0x85Printer 数据支持CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY的 MCU单独分配Maxim 平台还特意将 Bulk 端点放到 EP≥8 以利用更大 FIFO。高速HS配置usb_descriptors.c通过TUD_OPT_HIGH_SPEED条件编译启用CDC 与 Printer 端点包大小均提升至 512 字节同时提供 device qualifier 与 other-speed 配置描述符由tud_descriptor_configuration_cb依据当前链路速度tud_speed_get()动态选择返回 FS 或 HS 描述符。tusb_config.h 配置缓冲区与端点尺寸的精调示例的 tusb_config.h 是理解配置要点的关键。启用类与缓冲区的核心宏如下与 README 一致#define CFG_TUD_CDC 1 #define CFG_TUD_PRINTER 1 #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_PRINTER_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) #define CFG_TUD_PRINTER_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)对照 src/class/printer/printer_device.c 的实现可以理解这些宏的落点每个 Printer 接口实例包含两个tu_edpt_stream_t流式端点对象各自的 FIFO 缓冲数组大小即由CFG_TUD_PRINTER_RX_BUFSIZE/CFG_TUD_PRINTER_TX_BUFSIZE决定uint8_t rx_ff_buf[CFG_TUD_PRINTER_RX_BUFSIZE]; uint8_t tx_ff_buf[CFG_TUD_PRINTER_TX_BUFSIZE];需要区分的两组宏*_BUFSIZEFIFO 缓冲总容量。上例中 FS 为 64 字节、HS 为 512 字节越大吞吐越高、占 RAM 越多*_EPSIZE单次端点传输的包大小默认由TUD_EPSIZE_BULK_MAX决定FS 64 / HS 512示例中 CDC 与 Printer 均显式定义为(TUD_OPT_HIGH_SPEED ? 512 : 64)。两个方向还各有一对独立配置可分别调整。其余配置沿用通用模板CFG_TUD_ENABLED 1、CFG_TUD_ENDPOINT0_SIZE 64且 HID/MSC/MIDI/VENDOR 全部置 0仅保留 CDC 与 Printer。应用主循环与双向转发实现main()main.c完成板级初始化与tusb_init()后在主循环中依次驱动三件事while (1) { tud_task(); // tinyusb device task printer_to_cdc_task(); // forward printer data to CDC cdc_to_printer_task(); // forward CDC data to printer }Printer → CDC 方向printer_to_cdc_task()main.c用tud_printer_read_available()查询 Printer RX FIFO 中是否有数据同时用tud_cdc_write_available()确认 CDC TX 空间足够以 64 字节块为单位、取两者较小值调用tud_printer_read()读取tud_cdc_write()写入 CDC TX FIFO 并tud_cdc_write_flush()立即触发传输。CDC → Printer 方向cdc_to_printer_task()main.c是对称逻辑先查tud_cdc_available()CDC RX 数据量与tud_printer_write_available()Printer TX FIFO 空间再用tud_cdc_read()读取并tud_printer_write()tud_printer_write_flush()写出。注意两个任务都通过TU_MIN(...)限制单次搬运量不超过 64 字节且不越过对方可用空间避免 FIFO 溢出缓冲大小为 FS 64 字节时单次搬运正好一包。示例还实现了tud_printer_rx_cb()回调main.c但转发实际依赖轮询任务完成回调仅作通知占位。这些 API 的单端口便捷封装tud_printer_read、tud_printer_write等在 src/class/printer/printer_device.h 中定义为对tud_printer_n_*多端口版本的#0接口封装底层读写最终落到tu_edpt_stream_read/writeprinter_device.c与 CDC 类共用同一套端点流式传输基础设施。底层驱动Printer 类的三个类级控制请求TinyUSB 的 Printer 类驱动src/class/printer/printer_device.c在printerd_control_xfer_cb()第 224-276 行中处理三类类特定请求请求码定义于 src/class/printer/printer.h请求码名称说明回调0x00GET_DEVICE_ID返回 IEEE 1284 Device IDwIndex高字节为接口号tud_printer_get_device_id_cb0x01GET_PORT_STATUS返回 1 字节端口状态wIndex低字节为接口号tud_printer_get_port_status_cb0x02SOFT_RESET请求打印机软复位tud_printer_soft_reset_cb所有回调均提供TU_ATTR_WEAK弱默认实现printer_device.c默认端口状态为0x18无错误 已选中 纸未空对应 printer.h 中tusb_printer_port_status_t位域not_error1、selected1、paper_empty0。端点中断处理printerd_xfer_cb第 278-307 行在 RX 收到数据后回调tud_printer_rx_cb()并自动续投下一个 RX 传输TX 完成后回调tud_printer_tx_complete_cb()并借助tu_edpt_stream_write_zlp_if_needed()处理 HS 下需要的 ZLP 边界。这套机制保证了上层读可用/写可用的查询始终准确。IEEE 1284 Device ID 详解数据格式设备通过 GET_DEVICE_ID 请求向主机返回设备标识内容定义在 main.cstatic const char printer_device_id[] \x00\x34 // total length 52 0x0034 (big-endian) MFG:TinyUSB; MDL:Printer to CDC; CMD:PS; CLS:PRINTER;;格式要点前两个字节为以大端序存储的总长度含这两个长度字节本身。这里0x00 0x34 52与字符串部分长度恰好吻合并有一行TU_VERIFY_STATIC(sizeof(printer_device_id) - 1 52, ...)在编译期静态断言校验长度不匹配会直接编译失败后续为KEY:VALUE;形式的 Device ID 字符串遵循 IEEE 1284 标准缩写键MFG制造商、MDL型号、CMD命令集此处为 PostScript、CLS设备类别PRINTER。底层应答流程从 printer_device.c 可以看出处理细节驱动在 SETUP 阶段调用tud_printer_get_device_id_cb()拿到缓冲区指针后同样按device_id[0]8 | device_id[1]解析总长度再通过tud_control_xfer()将整个 Device ID 作为控制传输数据返回。因此应用侧只需保证返回的缓冲前两字节为大端总长度且缓冲在传输完成前保持有效该示例用static const数组天然满足。Linux 下验证cat /sys/class/usbmisc/lp0/device/ieee1284_id预期输出MFG:TinyUSB;MDL:Printer to CDC;CMD:PS;CLS:PRINTER;Linux 主机侧测试从加载内核模块到双向验证前置条件# 加载 USB 打印机内核模块若尚未加载 sudo modprobe usblp # 确认设备节点存在 ls /dev/ttyACM* /dev/usb/lp*设备在主机上呈现两个节点/dev/ttyACM0CDC 串口与/dev/usb/lp0USB 打印机具体编号因系统枚举顺序而异。若usblp未加载或设备协议不匹配/dev/usb/lp*可能不会出现。测试 Printer → CDC主机写打印机从串口读# 终端 1读取 CDC cat /dev/ttyACM0 # 终端 2写入打印机 echo hello from printer /dev/usb/lp0 # hello from printer 出现在终端 1数据流主机 →/dev/usb/lp0→ 设备 Printer OUT 端点 →printer_to_cdc_task→ CDC TX →/dev/ttyACM0→ 终端 1。测试 CDC → Printer主机写串口从打印机读# 终端 1读取打印机 IN 端点 cat /dev/usb/lp0 # 终端 2写入 CDC echo hello from cdc /dev/ttyACM0 # hello from cdc 出现在终端 1数据流主机 →/dev/ttyACM0→ 设备 CDC RX →cdc_to_printer_task→ Printer IN 端点 →/dev/usb/lp0→ 终端 1。交互式双向测试# 终端 1打开 CDC 串口 minicom -D /dev/ttyACM0 # 终端 2发送到打印机 echo tinyusb print example /dev/usb/lp0 # 文本出现在 minicom 中在 minicom 中键入内容可通过打印机 TX 返回该测试同时验证两个方向的实时转发适合快速确认设备链路与 FIFO 行为。构建与运行方式示例遵循 TinyUSB 示例的统一构建体系Make 方式在 examples/device/printer_to_cdc/Makefile 所在目录执行make BOARD...通过BOARD_TUD_RHPORT/BOARD_TUD_MAX_SPEED见 tusb_config.h可覆盖默认的设备端口与速度CMake 方式使用 examples/device/printer_to_cdc/CMakeLists.txt 配合 CMakePresets.json 选择板级预设。具体支持的板级列表可参考 hw/bsp/board_api.h 与板级构建文件。需要注意本示例未包含在skip.txt中属于默认参与构建的示例。小结printer_to_cdc是一个结构精炼但信息量完整的 TinyUSB 参考实现它以两个模板宏拼出 CDC Printer 复合描述符用两个几十行的任务函数实现双向桥接并通过 Printer 类驱动的弱回调与 IEEE 1284 Device ID 回调展示了打印机类设备的标准交互方式。无论是调试打印机驱动、验证打印数据流还是作为双向协议打印机设备的起点该示例都能直接上手并配合 Linuxusblp完成闭环验证。关键参考文件示例说明文档examples/device/printer_to_cdc/README.md应用逻辑与 Device IDexamples/device/printer_to_cdc/src/main.c配置宏examples/device/printer_to_cdc/src/tusb_config.h描述符实现examples/device/printer_to_cdc/src/usb_descriptors.cPrinter 类驱动src/class/printer/printer_device.c、src/class/printer/printer.h描述符模板宏src/device/usbd.h赞分享嵌入式驱动开发通信物联网【免费下载链接】tinyusbAn open source cross-platform USB stack for embedded system项目地址https://gitcode.com/gh_mirrors/ti/tinyusb点击查看免费下载相关推荐TinyUSB CDC 复合设备开发指南轻松实现虚拟串口与数据透传应用TinyUSB CDC 复合设备开发指南轻松实现虚拟串口与数据透传应用 TinyUSB 是一款开源的跨平台 USB 协议栈专为嵌入式系统设计支持 CDC嵌入式驱动开发通信物联网Strimzi Kafka Operator 命名空间级 RBAC 部署模式从 NamespaceRbacScopeOperatorST 系统测试到 YAML 安装转换机制Strimzi Kafka Operator 命名空间级 RBAC 部署模式从 NamespaceRbacScopeOperatorST 系统测试到 YAML嵌入式驱动开发通信物联网gog gmail track setup 深度解析在 gogcli 中一条命令搭起 Gmail 邮件打开追踪基础设施gog gmail track setup 深度解析在 gogcli 中一条命令搭起 Gmail 邮件打开追踪基础设施 本文以 gog gmail track嵌入式驱动开发通信物联网上一篇Python大麦网抢票脚本5分钟实现演唱会门票自动抢购下一篇大麦网抢票终极指南Python自动化脚本5分钟快速上手创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考