Files
arcs/tools/audio/usb_cdc_quick_start.md
2026-08-13 16:50:52 +08:00

11 KiB
Raw Blame History

USB CDC 录音系统快速开始指南

概述

本文档介绍如何使用新的USB CDC串口录音系统该系统使用自定义二进制协议进行通信支持

  • 命令控制(开始/停止录音、查询状态)
  • 音频数据传输(带序列号防丢包)
  • MD5校验确保数据完整性
  • 无需依赖adb shell命令

系统架构

┌──────────────┐                    ┌──────────────┐
│   PC端       │                    │   设备端     │
│              │                    │              │
│  Python脚本  │ ◄──USB CDC协议──► │  C代码       │
│              │                    │              │
│ - 发送命令   │                    │ - 接收命令   │
│ - 接收数据   │                    │ - 发送音频   │
│ - 计算MD5    │                    │ - 计算MD5    │
└──────────────┘                    └──────────────┘

文件说明

协议相关文件

文件 说明
docs/usb_cdc_protocol.md 完整的协议规范文档
src/middleware/usb/cdc_protocol.h 协议头文件C
src/middleware/usb/cdc_protocol.c 协议实现C
src/middleware/usb/app_usb_cdc.h USB CDC接口头文件
src/middleware/usb/app_usb_cdc.c USB CDC实现文件
tools/audio/serial_capture.py PC端录音脚本Python

PC端使用说明

1. 环境准备

确保已安装Python 3和pyserial库

# 安装依赖
pip3 install pyserial

# 或者使用系统包管理器
# Ubuntu/Debian
sudo apt-get install python3-serial

# macOS
brew install python3
pip3 install pyserial

2. 查找串口设备

Linux:

# 查看所有串口设备
ls /dev/ttyACM* /dev/ttyUSB*

# 通常USB CDC设备显示为 /dev/ttyACM0

Windows:

# 在设备管理器中查看 "端口(COM和LPT)"
# 通常显示为 COM3, COM4 等

macOS:

# 查看所有串口设备
ls /dev/tty.*

# USB CDC设备通常显示为 /dev/tty.usbmodemXXXX

3. 录音操作

基本用法:

# Linux
python3 tools/audio/serial_capture.py -p /dev/ttyACM0 -b 115200 -o output.pcm

# Windows
python tools/audio/serial_capture.py -p COM3 -b 115200 -o output.pcm

# macOS
python3 tools/audio/serial_capture.py -p /dev/tty.usbmodem14201 -b 115200 -o output.pcm

完整示例:

# 指定所有参数
python3 tools/audio/serial_capture.py \
    --port /dev/ttyACM0 \
    --baudrate 115200 \
    --output recordings/audio_$(date +%Y%m%d_%H%M%S).pcm \
    --timeout 2

4. 录音流程

脚本运行后会自动执行以下步骤:

  1. 连接串口

    [2026-01-07 10:30:15] 串口已打开: /dev/ttyACM0
    波特率: 115200
    输出文件: output.pcm
    --------------------------------------------------
    串口缓存已清空
    
  2. 发送开始命令

    --------------------------------------------------
    [命令] 发送: 开始录音
    [响应] 开始录音: 成功
    开始接收音频数据... (按 Ctrl+C 停止)
    --------------------------------------------------
    
  3. 接收音频数据

    已接收: 102400 字节 (序列号: 12)
    
  4. 停止录音按Ctrl+C

    用户中断
    
    停止录音...
    --------------------------------------------------
    [命令] 发送: 停止录音
    等待设备发送MD5...
    [MD5] 设备端: a1b2c3d4e5f6789012345678abcdef01
    [响应] 停止录音: 成功
    
  5. 验证MD5

    --------------------------------------------------
    录音完成!
    总字节数: 102400
    总帧数: 13
    PC端MD5: a1b2c3d4e5f6789012345678abcdef01
    设备端MD5: a1b2c3d4e5f6789012345678abcdef01
    MD5校验: 通过 ✓
    --------------------------------------------------
    

5. 播放录音

录音文件为原始PCM格式可以使用以下工具播放

使用ffplay推荐

# 16kHz, 16位, 单声道
ffplay -f s16le -ar 16000 -ac 1 output.pcm

# 48kHz, 16位, 双声道
ffplay -f s16le -ar 48000 -ac 2 output.pcm

转换为WAV格式

# 使用ffmpeg转换
ffmpeg -f s16le -ar 16000 -ac 1 -i output.pcm output.wav

使用sox播放

play -t raw -r 16000 -e signed -b 16 -c 1 output.pcm

设备端使用说明

1. 添加源文件到编译系统

确保以下文件被包含在编译中:

# CMakeLists.txt
target_sources(your_target PRIVATE
    src/middleware/usb/cdc_protocol.c
    src/middleware/usb/app_usb_cdc.c
)

target_include_directories(your_target PRIVATE
    src/middleware/usb
)

2. 初始化USB CDC

在应用初始化代码中调用:

#include "app_usb_cdc.h"

// 在main或init函数中
int ret = app_usb_cdc_init();
if (ret != 0) {
    LISA_LOGE("APP", "Failed to initialize USB CDC");
    return ret;
}

3. 发送音频数据

在音频采集回调中发送数据:

#include "app_usb_cdc.h"

void audio_callback(uint8_t *pcm_data, uint32_t len)
{
    // 只有在录音状态才发送
    if (app_usb_cdc_is_recording()) {
        // 数据会被自动封装成协议帧并发送
        app_usb_cdc_audio_write(pcm_data, len);
    }
}

4. 配置MD5计算可选

如果系统中有mbedtls可以在编译配置中启用

# Kconfig或.config
CONFIG_MBEDTLS=y

如果没有mbedtls代码会使用占位函数返回全0建议集成一个MD5实现。

5. 查看状态

// 检查是否正在录音
bool recording = app_usb_cdc_is_recording();

// 获取已发送的字节数
uint32_t bytes = app_usb_cdc_get_total_bytes();

// 获取当前序列号
uint32_t seq = app_usb_cdc_get_seq_num();

LISA_LOGI("APP", "Recording=%d, Bytes=%lu, Seq=%lu",
          recording, bytes, seq);

协议详解

基本帧格式

+--------+--------+--------+--------+--------+--------+
| Magic  | Type   |    Length       |  Data  | Check  |
| 2 Bytes| 1 Byte |    4 Bytes      | N Bytes| 1 Byte |
+--------+--------+--------+--------+--------+--------+
  0xAA55    0xXX    Little Endian      ...     XOR

数据类型

  • 0x01 - 命令请求PC → 设备)
  • 0x02 - 命令响应(设备 → PC
  • 0x03 - 音频数据(设备 → PC
  • 0x04 - MD5数据设备 → PC

命令码

  • 0x01 - 开始录音
  • 0x02 - 停止录音
  • 0x03 - 查询状态

详细协议说明请参考:usb_cdc_protocol.md

常见问题

Q1: 串口打开失败

错误: 串口错误: [Errno 13] Permission denied: '/dev/ttyACM0'

解决方法:

# 临时解决添加当前用户到dialout组
sudo usermod -a -G dialout $USER
# 注销并重新登录

# 或者临时使用sudo不推荐
sudo python3 tools/audio/serial_capture.py -p /dev/ttyACM0 -b 115200 -o output.pcm

Q2: MD5校验失败

可能原因:

  1. 设备端没有正确实现MD5计算使用的是占位函数
  2. 传输过程中有数据丢失
  3. 序列号跳变

解决方法:

  1. 在设备端集成完整的MD5库mbedtls或其他
  2. 检查是否有序列号跳变的警告
  3. 尝试降低波特率或使用更短的传输距离

Q3: 数据接收不完整

症状: 序列号跳变警告

解决方法:

  1. 检查USB连接是否稳定
  2. 降低音频数据发送速率
  3. 增大设备端的队列长度(CDC_AUDIO_QUEUE_LENGTH
  4. 检查PC端CPU占用率

Q4: 设备无响应

检查步骤:

# 1. 确认设备已连接
ls -l /dev/ttyACM*

# 2. 检查设备是否可读写
cat /dev/ttyACM0
# 应该能看到一些数据(如果设备在发送)

# 3. 尝试手动发送命令(用于调试)
echo -ne '\xAA\x55\x01\x01\x00\x00\x00\x01\xFA' > /dev/ttyACM0

Q5: 如何调试协议

PC端调试

在脚本中添加调试输出:

# 在parse_byte函数中添加
print(f"State: {self.state}, Byte: 0x{byte:02X}")

设备端调试:

// 启用协议日志
#define CDC_PROTO_DEBUG 1

// 在关键位置添加日志
LISA_LOGI(TAG, "Frame type: 0x%02X, length: %lu",
          frame->header.type, frame->header.length);

性能参数

典型配置

参数 说明
波特率 115200 标准USB CDC速度
音频帧大小 1024-4096字节 推荐2048字节
队列长度 50 可根据内存调整
最大数据长度 4096字节 单帧最大负载

理论吞吐量

波特率: 115200 bps
有效数据率: ~11.5 KB/s (考虑协议开销)

单帧开销: 8字节帧头+校验)+ 4字节序列号= 12字节
数据负载: 2048字节
帧效率: 2048 / (2048 + 12) ≈ 99.4%

支持音频格式:
- 16kHz, 16bit, Mono: 32 KB/s (需要更高波特率)
- 8kHz, 16bit, Mono: 16 KB/s (可支持)

建议: 对于高采样率音频如16kHz以上考虑使用更高的波特率或USB bulk传输模式。

扩展开发

添加新命令

  1. 在协议头文件中定义命令码:

    // cdc_protocol.h
    typedef enum {
        CDC_CMD_START_RECORD    = 0x01,
        CDC_CMD_STOP_RECORD     = 0x02,
        CDC_CMD_QUERY_STATUS    = 0x03,
        CDC_CMD_SET_GAIN        = 0x04,  // 新命令:设置增益
    } cdc_cmd_id_t;
    
  2. 在设备端实现命令处理:

    // app_usb_cdc.c
    case CDC_CMD_SET_GAIN:
        if (frame->header.length >= 2) {
            uint8_t gain = cmd->params[0];
            // 处理增益设置
            set_audio_gain(gain);
            response_len = cdc_proto_build_cmd_response(...);
        }
        break;
    
  3. 在PC端脚本中添加命令

    # serial_capture.py
    class CommandID(IntEnum):
        START_RECORD = 0x01
        STOP_RECORD = 0x02
        QUERY_STATUS = 0x03
        SET_GAIN = 0x04  # 新命令
    
    # 添加发送方法
    def set_gain(self, gain_value):
        params = bytes([gain_value])
        return self.send_command(CommandID.SET_GAIN, params)
    

参考资料

技术支持

如有问题或建议,请:

  1. 查阅协议文档:docs/usb_cdc_protocol.md
  2. 查看代码注释头文件中有详细的API说明
  3. 联系开发团队

版本: 1.0 更新日期: 2026-01-07 作者: LISTENAI开发团队